99. Recover Binary Search Tree (Hard)

Description:

Two elements of a binary search tree (BST) are swapped by mistake.

Recover the tree without changing its structure.

Example 1:

Example 2:

Follow up:

  • A solution using O(n) space is pretty straight forward.
  • Could you devise a constant space solution?

Solutions:

野路子Solution: 雖然解出來了返干,但是果然運(yùn)行速度太慢了

NOTE: 先用dfs遍歷二叉樹键痛,不斷check每個node丝里,標(biāo)記出來有問題的node。如果搜索到底部钮热,則保存當(dāng)前move_hist到move_ls,當(dāng)前correct_hist到corr_ls烛芬。 找出1-2個最特征的move_hist隧期。如果是2個飒责,則說明錯誤的兩個node在某個parent的左右兩邊,否則說明在一條鏈上仆潮。

  • case1: 如果是左右兩邊則第兩條move_hist第一個出問題的node的value互換宏蛉。
  • case2: 如果是在一條鏈上,則需要利用move_hist構(gòu)建在該鏈上的當(dāng)前錯誤的排序性置,同時保存node和node.val到兩個list拾并,然后對node.val的list排序,最后把這些值賦給node的list鹏浅。

一些測試集:

  • [20,35,30,5,15,25,10,3,7,13,17,23,27,33,37]
  • [30,10,20,5,15,25,35,3,7,13,17,23,27,33,37]
  • [27,10,30,5,15,25,35,3,7,13,17,23,20,33,37,null,null,null,null,null,null,null,null,null,null,26,28]
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def recoverTree(self, root: TreeNode) -> None:
        """
        Do not return anything, modify root in-place instead.
        """
        def check(node,mini,maxi):
            if node == None:
                return [True,True]
            error = [None,None]
            if node.left != None and (node.left.val > node.val or node.left.val < mini):
                error[0] = False
            else:
                error[0] = True
            if node.right != None and (node.right.val < node.val or node.right.val > maxi):
                error[1] = False
            else:
                error[1] = True
            return error
        
        corr_ls = []
        move_ls = []
        def dfs(move_hist,correct_hist,node,mini,maxi):
            left_corr,right_corr = check(node,mini,maxi)
            
            if node.left != None:
                if left_corr == False:
                    dfs(move_hist+"l",correct_hist+[left_corr],node.left,-math.inf,math.inf)
                else:
                    dfs(move_hist+"l",correct_hist+[left_corr],node.left,mini,node.val)
            if node.right != None:
                if right_corr == False:
                    dfs(move_hist+"r",correct_hist+[right_corr],node.right,-math.inf,math.inf)
                else:
                    dfs(move_hist+"r",correct_hist+[right_corr],node.right,node.val,maxi)
            if node.left == None and node.right == None:
                corr_ls.append(correct_hist)
                move_ls.append(move_hist)
        
        dfs("",[],root,-math.inf,math.inf)
        
        problem_path = []
        problem_corr = []
        for i in range(len(corr_ls)-1,-1,-1):
            if False not in corr_ls[I]:
                continue
            for j in range(len(corr_ls[i])-1,-1,-1):
                if corr_ls[i][j] == False:
                    break
            cache = move_ls[i][:j+1]
            if not problem_path:
                problem_path.append(cache)
                problem_corr.append(corr_ls[I])
            else:
                if len(cache) > len(problem_path[-1]):
                    if problem_path[-1] == cache[:len(problem_path[-1])]:
                        problem_path[-1] = cache
                        problem_corr[-1] = corr_ls[I]
                    else:
                        problem_path.append(cache)
                        problem_corr.append(corr_ls[I])
                elif problem_path[-1][:len(cache)] != cache:
                    problem_path.append(cache)
                    problem_corr.append(corr_ls[I])
                    
#         print(problem_path,"\n",problem_corr)
        
        def navigate(path,corr):
            cache = root
            for i,s in enumerate(path):
                if s == "l":
                    cache = cache.left
                else:
                    cache = cache.right
                if corr[i] == False:
                    break
            return cache
        
        def rearrange(path):
            cache = root
            value = [root.val]
            node = [root]
            last = 0
            for s in path:
                if s == "l":
                    cache = cache.left
                    node = node[:last] + [cache] + node[last:]
                    value = value[:last] + [cache.val] + value[last:]
                else:
                    cache = cache.right
                    node = node[:last+1] + [cache] + node[last+1:]
                    value = value[:last+1] + [cache.val] + value[last+1:]
                    last += 1
                    
            # print(value)
            value.sort()
            # print(value)
            for i in range(len(value)):
                node[i].val = value[I]
        
        if len(problem_path) == 2:
            n1 = navigate(problem_path[0],problem_corr[0])
            n2 = navigate(problem_path[1],problem_corr[1])
            # print(n1.val,n2.val)
            n1.val,n2.val = n2.val,n1.val
            # print(n1.val,n2.val)
        else:
            rearrange(problem_path[0])

Runtime: 104 ms, faster than 5.23% of Python3 online submissions for Recover Binary Search Tree.
Memory Usage: 13.7 MB, less than 5.78% of Python3 online submissions for Recover Binary Search Tree.

Solution2: O(n), inspired by https://www.cnblogs.com/grandyang/p/4298069.html 解法1

class Solution:
    def recoverTree(self, root: TreeNode) -> None:
        """
        Do not return anything, modify root in-place instead.
        """
        
        def dfs(node):
            if node.left == None and node.right == None:
                return [node.val],[node]
            val = []
            nod = []
            if node.left != None:
                left_ret = dfs(node.left)
                val += left_ret[0]
                nod += left_ret[1]
            val += [node.val]
            nod += [node]
            if node.right != None:
                right_ret = dfs(node.right)
                val += right_ret[0]
                nod += right_ret[1]
            return val,nod
        
        value_ls,node_ls = dfs(root)
        value_ls.sort()
        for i,n in enumerate(node_ls):
            n.val = value_ls[I]

Runtime: 76 ms, faster than 74.21% of Python3 online submissions for Recover Binary Search Tree.
Memory Usage: 13.5 MB, less than 40.00% of Python3 online submissions for Recover Binary Search Tree.

Solution3: inorder traversal, O(n) space 不用遞歸

class Solution:
    def recoverTree(self, root: TreeNode) -> None:
        """
        Do not return anything, modify root in-place instead.
        """
        
        value = []
        node = []
        stack = []
        curr = root
        while curr != None or stack:
            if curr != None:
                stack.append(curr)
                curr = curr.left
            else:
                curr = stack.pop()
                value.append(curr.val)
                node.append(curr)
                curr = curr.right
                
        value.sort()
        for i,n in enumerate(node):
            n.val = value[I]

Runtime: 80 ms, faster than 53.35% of Python3 online submissions for Recover Binary Search Tree.
Memory Usage: 13.5 MB, less than 40.00% of Python3 online submissions for Recover Binary Search Tree.

Solution4: Morris inorder traversal O(1) space

inspired by http://fisherlei.blogspot.com/2012/12/leetcode-recover-binary-search-tree.html and
https://www.geeksforgeeks.org/fix-two-swapped-nodes-of-bst/

NOTE: 不用保存完整的traversal history嗅义,只需要保存last visited node(叫prev)就行了,這個last node初始化是None隐砸。然后凡是prev比curr大之碗,就把這倆按順序放到一個list里(叫problem)。兩種情況:



不論那種情況季希,最終只需要把problem的開頭和結(jié)尾的node的value互換就行了褪那!

class Solution:
    def recoverTree(self, root: TreeNode) -> None:
        """
        Do not return anything, modify root in-place instead.
        """
        
        curr = root
        prev = None
        
        def FindPredecessor(node):
            cache = node.left
            while cache.right != None and cache.right != node:
                cache = cache.right
            return cache
        
        problem = []
        
        while curr != None:
            if curr.left == None:
                if prev != None and curr.val < prev.val: # 先比較
                    problem += [prev,curr] # 先比較
                prev = curr # 先比較,之前這一步是ret.append(curr.val)
                curr = curr.right
            else:
                pred = FindPredecessor(curr)
                if pred.right == None:
                    pred.right = curr
                    curr = curr.left
                else:
                    if prev != None and curr.val < prev.val: # 先比較
                        problem += [prev,curr] # 先比較
                    prev = curr # 先比較胖眷,之前這一步是ret.append(curr.val)
                    curr = curr.right
                    pred.right = None
                    
        p1,p2 = problem[0],problem[-1]
        p1.val,p2.val = p2.val,p1.val

Runtime: 68 ms, faster than 95.79% of Python3 online submissions for Recover Binary Search Tree.
Memory Usage: 13.3 MB, less than 90.00% of Python3 online submissions for Recover Binary Search Tree.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末武通,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子珊搀,更是在濱河造成了極大的恐慌冶忱,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件境析,死亡現(xiàn)場離奇詭異囚枪,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)劳淆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門链沼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人沛鸵,你說我怎么就攤上這事括勺。” “怎么了曲掰?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵疾捍,是天一觀的道長。 經(jīng)常有香客問我栏妖,道長乱豆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任吊趾,我火速辦了婚禮宛裕,結(jié)果婚禮上瑟啃,老公的妹妹穿的比我還像新娘。我一直安慰自己揩尸,他們只是感情好蛹屿,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疲酌,像睡著了一般蜡峰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上朗恳,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天湿颅,我揣著相機(jī)與錄音,去河邊找鬼粥诫。 笑死油航,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的怀浆。 我是一名探鬼主播谊囚,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼执赡!你這毒婦竟也來了镰踏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤沙合,失蹤者是張志新(化名)和其女友劉穎奠伪,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體首懈,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绊率,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了究履。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滤否。...
    茶點(diǎn)故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖最仑,靈堂內(nèi)的尸體忽然破棺而出藐俺,到底是詐尸還是另有隱情,我是刑警寧澤泥彤,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布紊搪,位于F島的核電站,受9級特大地震影響全景,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜牵囤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一爸黄、第九天 我趴在偏房一處隱蔽的房頂上張望滞伟。 院中可真熱鬧,春花似錦炕贵、人聲如沸梆奈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亩钟。三九已至,卻和暖如春鳖轰,著一層夾襖步出監(jiān)牢的瞬間清酥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工蕴侣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留焰轻,地道東北人。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓昆雀,卻偏偏與公主長得像辱志,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子狞膘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評論 2 350

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