二叉樹(shù) 18 (尋找重復(fù)的子樹(shù) leetcode 652)

思想

二叉樹(shù)的核心思想是分治和遞歸蜓竹,特點(diǎn)是遍歷方式。
解題方式常見(jiàn)兩類(lèi)思路:

  1. 遍歷一遍二叉樹(shù)尋找答案储藐;
  2. 通過(guò)分治分解問(wèn)題尋求答案俱济;

遍歷分為前中后序,本質(zhì)上是遍歷二叉樹(shù)過(guò)程中處理每個(gè)節(jié)點(diǎn)的三個(gè)特殊時(shí)間點(diǎn):

  1. 前序是在剛剛進(jìn)入二叉樹(shù)節(jié)點(diǎn)時(shí)執(zhí)行钙勃;
  2. 后序是在將要離開(kāi)二叉樹(shù)節(jié)點(diǎn)時(shí)執(zhí)行蛛碌;
  3. 中序是左子樹(shù)遍歷完進(jìn)入右子樹(shù)前執(zhí)行;
# 前序
     1 node
    /      \
 2 left   3 right
中左右
 
# 中序
     2 node
    /      \
 1 left    3 right
左中右
 
# 后序
     3 node
    /      \
 1 left    2 right     
左右中       

多叉樹(shù)只有前后序列遍歷辖源,因?yàn)橹挥卸鏄?shù)有唯一一次中間節(jié)點(diǎn)的遍歷

題目的關(guān)鍵就是找到遍歷過(guò)程中的位置蔚携,插入對(duì)應(yīng)代碼邏輯實(shí)現(xiàn)場(chǎng)景的目的。

實(shí)例

尋找重復(fù)的子樹(shù) leetcode 652

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

輸入:
TreeNode同木,一棵樹(shù)的根節(jié)點(diǎn)

輸出:
List[TreeNode]浮梢,返回所有重復(fù)的子樹(shù)列表

舉例:
輸入 root = [1,2,3,4,null,2,4,null,null,4]
返回二叉樹(shù)子樹(shù)列表 [[2,4],[4]]

    1                 
   / \               
  2   3         
 /   / \          
4   2   4
   /
  4        

二叉樹(shù)的數(shù)據(jù)存儲(chǔ)可以使用鏈表跛十,也可以使用數(shù)組彤路,往往數(shù)組更容易表達(dá),根節(jié)點(diǎn)從 index=1 處開(kāi)始存儲(chǔ)芥映,浪費(fèi) index=0 的位置
left_child = 2 * parent
right_child = 2 * parent + 1
parent = child // 2

分治解

查找重復(fù)子樹(shù)洲尊,要在獲得子樹(shù)詳細(xì)情況的位置進(jìn)行比對(duì)远豺,所以在后續(xù)遍歷的位置比對(duì)是否出現(xiàn)了重復(fù)子樹(shù)。全局一塊內(nèi)存儲(chǔ)存出現(xiàn)過(guò)的子樹(shù)坞嘀。

上例中躯护,全局儲(chǔ)存子樹(shù) [],重復(fù)子樹(shù) []

  • 根節(jié)點(diǎn) 1 出發(fā)丽涩,在后續(xù)遍歷的位置進(jìn)行比對(duì)
  • 1 遍歷到左子樹(shù) 2棺滞,再到左子樹(shù) 4,左右子樹(shù)都為空矢渊,返回到 2
  • 2 的右子樹(shù)為空继准,左子樹(shù) 4,存儲(chǔ)到全局子樹(shù)中 [[4]]矮男,返回到根節(jié)點(diǎn) 1
  • 繼續(xù)遍歷 1 的右子樹(shù) 3移必,到 3 的左子樹(shù) 2,再到 2 的左子樹(shù) 4 返回
  • 2 的左子樹(shù) 4 存在于全局子樹(shù)毡鉴,重復(fù)子樹(shù)增加 [[4]]崔泵,返回 3
  • 繼續(xù)遍歷 3 的右子樹(shù) 4,左右子樹(shù)均為空猪瞬,返回 3
  • 3 的左子樹(shù) [2,4] 加入全局存儲(chǔ)子樹(shù)憎瘸,[[4], [2,4]],右子樹(shù)存在于重復(fù)子樹(shù)不再處理陈瘦,返回根節(jié)點(diǎn) 1
  • 1 的左子樹(shù) [2,4] 重復(fù)含思,加入重復(fù)子樹(shù) [[4], [2,4]],右子樹(shù) [3,2,4,4] 加入全局子樹(shù) [[4], [2,4], [3,2,4,4]]
  • 結(jié)束遍歷甘晤,最終返回 [[2,4],[4]]

編碼


from typing import Optional, List


class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right


def find_duplicate_subtrees(root: Optional[TreeNode]) -> List[Optional[TreeNode]]:
    # 儲(chǔ)存全局子樹(shù)
    subtree, duplicate = {}, []

    def serialize(root: Optional[TreeNode]) -> str:
        nodes = []

        def _traverse(node: Optional[TreeNode]):
            if node is None:
                nodes.append('#')
                return
            nodes.append(str(node.val))
            _traverse(node.left)
            _traverse(node.right)

        _traverse(root)
        return ','.join(nodes)

    def traverse(root: Optional[TreeNode]):
        # base 條件含潘,葉子空節(jié)點(diǎn)直接返回
        if root is None:
            return
        traverse(root.left)
        traverse(root.right)
        # 后序位置做子樹(shù)比對(duì)
        serialize_left = serialize(root.left)
        serialize_right = serialize(root.right)
        if serialize_left not in subtree:
            subtree[serialize_left] = 0
        else:
            subtree[serialize_left] += 1
            if subtree[serialize_left] == 1:
                duplicate.append(root.left)
        if serialize_right not in subtree:
            subtree[serialize_right] = 0
        else:
            subtree[serialize_right] += 1
            if subtree[serialize_right] == 1:
                duplicate.append(root.right)

    traverse(root)
    # 過(guò)濾空子樹(shù)
    return [e for e in duplicate if e]


def find_duplicate_subtrees_optimize(root: Optional[TreeNode]) -> List[Optional[TreeNode]]:
    # 儲(chǔ)存全局子樹(shù)
    subtree, duplicate = {}, []

    def traverse(root: Optional[TreeNode]) -> str:
        # base 條件,葉子空節(jié)點(diǎn)返回占位符
        if root is None:
            return '#'
        left = traverse(root.left)
        right = traverse(root.right)
        # 后序位置做子樹(shù)比對(duì)
        tree = f'{left},{right},{root.val}'
        if tree not in subtree:
            subtree[tree] = 0
        else:
            subtree[tree] += 1
            if subtree[tree] == 1:
                duplicate.append(root)
        return tree

    traverse(root)
    return duplicate

相關(guān)

二叉樹(shù) 0
二叉樹(shù) 1
二叉樹(shù) 2
二叉樹(shù) 3
二叉樹(shù) 4
二叉樹(shù) 5
二叉樹(shù) 6
二叉樹(shù) 7
二叉樹(shù) 8
二叉樹(shù) 9
二叉樹(shù) 10
二叉樹(shù) 11
二叉樹(shù) 12
二叉樹(shù) 13
二叉樹(shù) 14
二叉樹(shù) 15
二叉樹(shù) 16
二叉樹(shù) 17

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末线婚,一起剝皮案震驚了整個(gè)濱河市遏弱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌塞弊,老刑警劉巖漱逸,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異游沿,居然都是意外死亡饰抒,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)诀黍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)袋坑,“玉大人,你說(shuō)我怎么就攤上這事眯勾≡婀” “怎么了婆誓?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)也颤。 經(jīng)常有香客問(wèn)我洋幻,道長(zhǎng),這世上最難降的妖魔是什么翅娶? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任文留,我火速辦了婚禮,結(jié)果婚禮上竭沫,老公的妹妹穿的比我還像新娘厂庇。我一直安慰自己,他們只是感情好输吏,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布权旷。 她就那樣靜靜地躺著,像睡著了一般贯溅。 火紅的嫁衣襯著肌膚如雪拄氯。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天它浅,我揣著相機(jī)與錄音译柏,去河邊找鬼。 笑死姐霍,一個(gè)胖子當(dāng)著我的面吹牛鄙麦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播镊折,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼胯府,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了恨胚?” 一聲冷哼從身側(cè)響起骂因,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎赃泡,沒(méi)想到半個(gè)月后寒波,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡升熊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年俄烁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片级野。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡聊疲,死狀恐怖狮辽,靈堂內(nèi)的尸體忽然破棺而出坯认,到底是詐尸還是另有隱情,我是刑警寧澤矛双,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布渊抽,位于F島的核電站蟆豫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏懒闷。R本人自食惡果不足惜十减,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望愤估。 院中可真熱鬧帮辟,春花似錦、人聲如沸玩焰。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)昔园。三九已至蔓榄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間默刚,已是汗流浹背甥郑。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留荤西,地道東北人澜搅。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像邪锌,于是被迫代替她去往敵國(guó)和親勉躺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容