劍指 Offer 68 - II. 二叉樹的最近公共祖先

題目介紹

描述:

給定一個(gè)二叉樹, 找到該樹中兩個(gè)指定節(jié)點(diǎn)的最近公共祖先系吭。

百度百科中最近公共祖先的定義為:“對(duì)于有根樹 T 的兩個(gè)結(jié)點(diǎn) p笑窜、q璧函,最近公共祖先表示為一個(gè)結(jié)點(diǎn) x收苏,滿足 x 是 p君躺、q 的祖先且 x 的深度盡可能大(一個(gè)節(jié)點(diǎn)也可以是它自己的祖先)夸赫】美铮”

例如垮兑,給定如下二叉樹: root = [3,5,1,6,2,0,8,null,null,7,4]

示例 1:

輸入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
輸出: 3
解釋: 節(jié)點(diǎn) 5 和節(jié)點(diǎn) 1 的最近公共祖先是節(jié)點(diǎn) 3诉瓦。
示例 2:

輸入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
輸出: 5
解釋: 節(jié)點(diǎn) 5 和節(jié)點(diǎn) 4 的最近公共祖先是節(jié)點(diǎn) 5川队。因?yàn)楦鶕?jù)定義最近公共祖先節(jié)點(diǎn)可以為節(jié)點(diǎn)本身。

解題思路:

遞歸算法的關(guān)鍵是要明確函數(shù)的「定義」是什么睬澡,然后相信這個(gè)定義固额,利用這個(gè)定義推導(dǎo)最終結(jié)果。

寫樹相關(guān)的算法煞聪,簡單說就是斗躏,先搞清楚當(dāng)前 root 節(jié)點(diǎn)該做什么,然后根據(jù)函數(shù)定義遞歸調(diào)用子節(jié)點(diǎn)昔脯,遞歸調(diào)用會(huì)讓孩子節(jié)點(diǎn)做相同的事情啄糙。

二叉樹題目的一個(gè)難點(diǎn)在于如何通過題目的要求思考出每一個(gè)節(jié)點(diǎn)需要做什么

二叉樹解題策略

一 遞歸 二 隊(duì)列 + 迭代 (層次遍歷) 三 棧 + 迭代 (非遞歸遍歷) 四 其它

三種基本的遍歷方式,都可以用遞歸來實(shí)現(xiàn)栅干。寫遞歸算法的時(shí)候迈套,需要注意遞歸退出條件以及遞歸操作的表達(dá)。

本題和之前某題不一樣的地方是碱鳞,本題是二叉樹桑李,不是搜索二叉樹。

自己的解法實(shí)現(xiàn)

def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        if not root or root == p or root == q: return root
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        if left and right: return root
        return left or right

網(wǎng)上比較優(yōu)秀的解法

解法一

解題思路: 祖先的定義: 若節(jié)點(diǎn) p 在節(jié)點(diǎn) root 的左(右)子樹中窿给,或 p=root 贵白,則稱 root 是 p 的祖先。

最近公共祖先的定義: 設(shè)節(jié)點(diǎn) root 為節(jié)點(diǎn) p,q 的某公共祖先崩泡,若其左子節(jié)點(diǎn) root.left 和右子節(jié)點(diǎn) root.right 都不是 p,q 的公共祖先禁荒,則稱 root 是 “最近的公共祖先” 。

根據(jù)以上定義角撞,若 root 是 p,q 的 最近公共祖先 呛伴,則只可能為以下情況之一:

p 和 q 在 root 的子樹中勃痴,且分列 root 的 異側(cè)(即分別在左、右子樹中)热康; p=root 沛申,且 qq 在 root 的左或右子樹中; q=root 姐军,且 p 在 root 的左或右子樹中铁材;

考慮通過遞歸對(duì)二叉樹進(jìn)行后序遍歷,當(dāng)遇到節(jié)點(diǎn) p 或 q 時(shí)返回奕锌。從底至頂回溯著觉,當(dāng)節(jié)點(diǎn) p,q 在節(jié)點(diǎn) root 的異側(cè)時(shí),節(jié)點(diǎn) root 即為最近公共祖先惊暴,則向上返回 root 饼丘。

def lowestCommonAncestor(self, root, p, q):
        if not root or root == p or root == q: return root
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        if not left: return right
        if not right: return left
        return root

解法二

由于lowestCommonAncestor(root, p, q)的功能是找出以root為根節(jié)點(diǎn)的兩個(gè)節(jié)點(diǎn)p和q的最近公共祖先。 我們考慮:

  1. 如果p和q分別是root的左右節(jié)點(diǎn)辽话,那么root就是我們要找的最近公共祖先
  2. 如果root是None葬毫,說明我們?cè)谶@條尋址線路沒有找到,我們返回None表示沒找到 我們繼續(xù)在左右子樹執(zhí)行相同的邏輯屡穗。
  3. 如果左子樹沒找到,說明在右子樹忽肛,我們返回lowestCommonAncestor(root.right, p , q)
  4. 如果右子樹沒找到村砂,說明在左子樹,我們返回lowestCommonAncestor(root.left, p , q)
  5. 如果左子樹和右子樹分別找到一個(gè)屹逛,我們返回root
def lowestCommonAncestor2(self, root, p, q):
        if not root or root == p or root == q: return root
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        return root if left and right else left or right

解法三

1.將每個(gè)節(jié)點(diǎn)標(biāo)記础废,記錄在字典(哈希表)里,方式為:從根節(jié)點(diǎn)開始罕模,向左為0评腺,向右為1,如示例中節(jié)點(diǎn)7標(biāo)記為“010”淑掌,節(jié)點(diǎn)4標(biāo)記為“011” 2.由兩個(gè)節(jié)點(diǎn)的標(biāo)記從頭開始的公共部分得到祖先的標(biāo)記蒿讥,如7和4的祖先標(biāo)記為“01” 3.由祖先的標(biāo)記得到祖先節(jié)點(diǎn)2

def lowestCommonAncestor3(self, root, p, q):
        stack = [root]
        _dic = {root: ""}
        while stack:
            tmp = []
            for e in stack:
                if e.left:
                    _dic[e.left] = _dic[e] + "0"
                    tmp.append(e.left)
                if e.right:
                    _dic[e.right] = _dic[e] + "1"
                    tmp.append(e.right)
            stack = tmp
        # 標(biāo)記完成,接下來根據(jù)標(biāo)記找到pq的最近公共祖先的標(biāo)記
        p_tag = _dic[p]
        q_tag = _dic[q]
        num = 0
        res_tag = ""
        while num < len(p_tag) and num < len(q_tag):
            if p_tag[num] != q_tag[num]:
                continue
            else:
                res_tag += p_tag[num]
                num += 1
        for k, v in _dic.items():
            if v == res_tag:
                return k

相關(guān)知識(shí)總結(jié)和思考

相關(guān)知識(shí):

BFS:廣度/寬度優(yōu)先抛腕。其實(shí)就是從上到下芋绸,先把每一層遍歷完之后再遍歷一下一層。

可以使用Queue的數(shù)據(jù)結(jié)構(gòu)担敌。我們將root節(jié)點(diǎn)初始化進(jìn)隊(duì)列摔敛,通過消耗尾部,插入頭部的方式來完成BFS全封。

二叉搜索樹(BST)的特性:

  1. 若它的左子樹不為空马昙,則所有左子樹上的值均小于其根節(jié)點(diǎn)的值
  2. 若它的右子樹不為空桃犬,則所有右子樹上的值均大于其根節(jié)點(diǎn)的值
  3. 它的左右子樹也分別為二叉搜索樹

遞歸與迭代的區(qū)別

遞歸:重復(fù)調(diào)用函數(shù)自身實(shí)現(xiàn)循環(huán)稱為遞歸; 迭代:利用變量的原值推出新值稱為迭代行楞,或者說迭代是函數(shù)內(nèi)某段代碼實(shí)現(xiàn)循環(huán)攒暇;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市敢伸,隨后出現(xiàn)的幾起案子扯饶,更是在濱河造成了極大的恐慌,老刑警劉巖池颈,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尾序,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡躯砰,警方通過查閱死者的電腦和手機(jī)每币,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琢歇,“玉大人兰怠,你說我怎么就攤上這事±蠲#” “怎么了揭保?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長魄宏。 經(jīng)常有香客問我秸侣,道長,這世上最難降的妖魔是什么宠互? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任味榛,我火速辦了婚禮,結(jié)果婚禮上予跌,老公的妹妹穿的比我還像新娘搏色。我一直安慰自己,他們只是感情好券册,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布频轿。 她就那樣靜靜地躺著,像睡著了一般烁焙。 火紅的嫁衣襯著肌膚如雪略吨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天考阱,我揣著相機(jī)與錄音翠忠,去河邊找鬼。 笑死乞榨,一個(gè)胖子當(dāng)著我的面吹牛秽之,可吹牛的內(nèi)容都是我干的当娱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼考榨,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼跨细!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起河质,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤冀惭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后掀鹅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體散休,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年乐尊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了戚丸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扔嵌,死狀恐怖限府,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情痢缎,我是刑警寧澤胁勺,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站独旷,受9級(jí)特大地震影響姻几,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜势告,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望抚恒。 院中可真熱鬧咱台,春花似錦、人聲如沸俭驮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽混萝。三九已至遗遵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逸嘀,已是汗流浹背车要。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留崭倘,地道東北人翼岁。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓类垫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親琅坡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子悉患,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355