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: 虽然解出来了,但是果然运行速度太慢了

NOTE: 先用dfs遍历二叉树,不断check每个node,标记出来有问题的node。如果搜索到底部,则保存当前move_hist到move_ls,当前correct_hist到corr_ls。 找出1-2个最特征的move_hist。如果是2个,则说明错误的两个node在某个parent的左右两边,否则说明在一条链上。

  • case1: 如果是左右两边则第两条move_hist第一个出问题的node的value互换。
  • case2: 如果是在一条链上,则需要利用move_hist构建在该链上的当前错误的排序,同时保存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的开头和结尾的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.

你可能感兴趣的:(99. Recover Binary Search Tree (Hard))