Day 21 二叉樹: 530. 搜索樹最小絕對差, 501. 搜索樹眾數(shù), 236. 最近公共祖先

530. 二叉搜索樹的最小絕對差

  • 思路
    • example
    • 返回 樹中任意兩不同節(jié)點值之間的最小差值
    • 二叉搜索樹(BST): 對應(yīng)有序(遞增)數(shù)組 (中序遍歷)
    • 最小差值肯定在相鄰兩個元素間取得问欠。(如果求最大絕對差只需第1個和最后1個的絕對差)
    • 解法1:遞歸挠唆,中序遍歷轉(zhuǎn)化為有序數(shù)組兔沃,額外空間
      • 復(fù)雜度. 時間:O(n), 空間: O(n
class Solution:
    def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
        def traversal(root):
            if root == None:
                return 
            traversal(root.left)
            nums.append(root.val)
            traversal(root.right)
        nums = []
        traversal(root)
        res = float('inf')
        for i in range(1, len(nums)):
            res = min(res, abs(nums[i] - nums[i-1]))
        return res
  • 解法2:遞歸,中序遍歷审葬,維護(hù)pre節(jié)點
    • 可看成是在BST上的雙指針(pre, cur), 同向
class Solution:
    def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
        def traversal(root):
            nonlocal pre, res 
            if root == None:   
                return 
            traversal(root.left)
            if pre:
                res = min(res, abs(pre.val - root.val))
            pre = root 
            traversal(root.right) 
        pre = None  
        res = float('inf')
        traversal(root)
        return res 
  • 解法3:迭代,中序遍歷,維護(hù)pre節(jié)點
class Solution:
    def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
        cur = root 
        stack = []
        pre = None
        res = float('inf')
        while cur or stack:
            if cur: 
                stack.append(cur)
                cur = cur.left 
            else:
                cur = stack.pop()
                if pre:
                    res = min(res, abs(pre.val - cur.val))
                pre = cur 
                cur = cur.right 
        return res
class Solution:
    def minDiffInBST(self, root: Optional[TreeNode]) -> int:
        def traversal(root):
            nonlocal pre, res 
            if root == None:
                return 
            traversal(root.left)
            if pre:
                res = min(res, abs(root.val - pre.val))
            pre = root 
            traversal(root.right)
        pre = None 
        res = float('inf')
        traversal(root)
        return res

501. 二叉搜索樹中的眾數(shù)

  • 思路
    • example
    • 含重復(fù)值
    • 有序數(shù)組累計頻率
    • 遞歸倍踪,中序瓣窄,維護(hù)pre節(jié)點笛厦, 雙指針
      • pre節(jié)點
      • max_freq
      • cur_freq
      • res = [] 保存結(jié)果
  • 復(fù)雜度. 時間:O(n), 空間: O(1) (如果遞歸棧不算在內(nèi))
class Solution:
    def findMode(self, root: Optional[TreeNode]) -> List[int]:
        def traversal(root):
            nonlocal pre, cnt, cnt_max, res 
            if root == None:
                return 
            traversal(root.left)
            if pre:
                if root.val == pre.val:
                    cnt += 1
                else:
                    cnt = 1
            else:
                cnt = 1
            if cnt > cnt_max:
                cnt_max = cnt 
                res = [root.val]
            elif cnt == cnt_max:
                res.append(root.val)
            pre = root 
            traversal(root.right)
        cnt, cnt_max = -1, -float('inf')
        pre = None 
        res = []
        traversal(root)
        return res 
class Solution:
    def findMode(self, root: Optional[TreeNode]) -> List[int]:
        def traversal(root):
            nonlocal pre, cnt, max_cnt 
            nonlocal res # !!!
            if root == None:
                return 
            traversal(root.left)
            if pre == None:
                cnt = 1
            else:
                if root.val == pre.val:
                    cnt += 1
                else:
                    cnt = 1
            if cnt > max_cnt:
                max_cnt = cnt
                res = [root.val]
            elif cnt == max_cnt:
                res.append(root.val) 
            pre = root 
            traversal(root.right)
        res = []
        pre = None 
        cnt, max_cnt = 0, 0 
        traversal(root)
        return res 

236. 二叉樹的最近公共祖先

  • 思路
    • example
    • 所有 Node.val 互不相同.
    • p and q will exist in the tree.
    • 遞歸dfs,前序俺夕,回溯裳凸,自上而下贱鄙,保存根節(jié)點到p,q的兩條路徑
    • 在路徑中找最近公共祖先
      • 更直觀姨谷。
      • 比較多細(xì)節(jié)逗宁。
      • 適用更一般的情況。
  • 復(fù)雜度. 時間:O(n), 空間: O(n)
class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        def traversal(root, targetNode):
            if root == targetNode:
                paths.append(path[:])
                return 
            if root.left: 
                path.append(root.left)
                traversal(root.left, targetNode)
                path.pop() 
            if root.right: 
                path.append(root.right)
                traversal(root.right, targetNode) 
                path.pop()
        paths = [] # save path results
        path = [root] # local path
        traversal(root, p)
        traversal(root, q)
        ancestor = None
        for i in range(min(len(paths[0]), len(paths[1]))):
            if paths[0][i] == paths[1][i]:
                ancestor = paths[0][i]
            else:
                break  
        return ancestor
  • 上面的代碼遞歸找path的時候仍然遍歷了整棵樹梦湘。
  • 優(yōu)化:提早return
class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        def traversal(root, targetNode):
            if root == targetNode:
                paths.append(path[:])
                return True
            if root.left: 
                path.append(root.left)
                if traversal(root.left, targetNode):
                    return True
                path.pop() 
            if root.right: 
                path.append(root.right)
                if traversal(root.right, targetNode):
                    return True
                path.pop()
            return False
        paths = [] # save path results
        path = [root]
        traversal(root, p)
        path = [root] # 需要重新初始化O箍拧!捌议!
        traversal(root, q)
        ancestor = None
        for i in range(min(len(paths[0]), len(paths[1]))):
            if paths[0][i] == paths[1][i]:
                ancestor = paths[0][i]
            else:
                break  
        return ancestor
  • 遞歸言缤,后序自下而上禁灼,回溯

返回值:p, q, None, 或者LCA(p,q)

  • 遍歷整棵樹
  • 不直觀, 注意Base Case的邏輯管挟。
  • 前提:樹中必有p,q節(jié)點,并且樹中節(jié)點數(shù)值各不相同E丁Fⅰ!
  • def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
    • 函數(shù)意義:
      • 樹中含有p,q時守谓,返回最近公共祖先
      • 樹中只含p或q的一個時穿铆,返回相應(yīng)的節(jié)點
      • 樹中不含p和q時,返回None
    • 后序:結(jié)合左子樹與右子樹遍歷結(jié)果
      • 如果左子樹含有p,q斋荞。那么左子樹返回結(jié)果即為答案荞雏。如果右子樹含有p,q. 那到右子樹返回結(jié)果即為答案。
      • 如果左子樹只含q,q中的一個平酿,返回左子樹結(jié)果凤优。如果右子樹只含q,q中的一個,返回右子樹結(jié)果蜈彼。
class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if root == None:
            return None 
        if 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 
        if left == None:
            return right 
        if right == None:
            return left 
class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        def traversal(root):
            if root == None:
                return None 
            if root == p or root == q:
                return root 
            left = traversal(root.left) 
            right = traversal(root.right) 
            if left == None:
                return right 
            if right == None:
                return left 
            return root 
        return traversal(root) 
class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if root == None:
            return None   
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)  
        if root.val == p.val or root.val == q.val:
            return root  
        if left and right == None:
            return left  
        if left == None and right:
            return right 
        if left == None and right == None:
            return None  
        if left and right:
            return root  
class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        def traverse(root):
            if root == None:
                return None 
            left = traverse(root.left)
            right = traverse(root.right) 
            if root == p or root == q:
                return root 
            if left == p and right == q:
                return root 
            if left == q and right == p:
                return root 
            if left != None:
                return left 
            if right != None:
                return right 
            return None 
        return traverse(root)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末筑辨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子幸逆,更是在濱河造成了極大的恐慌棍辕,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件还绘,死亡現(xiàn)場離奇詭異楚昭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)拍顷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進(jìn)店門抚太,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人菇怀,你說我怎么就攤上這事凭舶∩慰椋” “怎么了?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵帅霜,是天一觀的道長匆背。 經(jīng)常有香客問我,道長身冀,這世上最難降的妖魔是什么钝尸? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮搂根,結(jié)果婚禮上珍促,老公的妹妹穿的比我還像新娘。我一直安慰自己剩愧,他們只是感情好猪叙,可當(dāng)我...
    茶點故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著仁卷,像睡著了一般穴翩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锦积,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天芒帕,我揣著相機(jī)與錄音,去河邊找鬼丰介。 笑死背蟆,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的哮幢。 我是一名探鬼主播带膀,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼家浇!你這毒婦竟也來了本砰?” 一聲冷哼從身側(cè)響起碴裙,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤钢悲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后舔株,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體莺琳,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年载慈,在試婚紗的時候發(fā)現(xiàn)自己被綠了惭等。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡办铡,死狀恐怖辞做,靈堂內(nèi)的尸體忽然破棺而出琳要,到底是詐尸還是另有隱情,我是刑警寧澤秤茅,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布稚补,位于F島的核電站,受9級特大地震影響框喳,放射性物質(zhì)發(fā)生泄漏课幕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一五垮、第九天 我趴在偏房一處隱蔽的房頂上張望乍惊。 院中可真熱鬧,春花似錦放仗、人聲如沸润绎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凡橱。三九已至,卻和暖如春亭姥,著一層夾襖步出監(jiān)牢的瞬間稼钩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工达罗, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留坝撑,地道東北人。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓粮揉,卻偏偏與公主長得像巡李,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子扶认,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,876評論 2 361

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