99. Recover Binary Search Tree

题目:基础题,代码里给出了三种解答方法,其中Morris法是O(1)space。

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

Recover the tree without changing its structure.

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

代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    //recursion  方法:
    TreeNode* first;
    TreeNode* second;
    TreeNode* pre;
    void recoverTree(TreeNode* root) {
        inorder(root);
        int temp = first->val;
        first->val = second->val;
        second->val = temp;
        
    }
    void inorder(TreeNode* root){
        if(root == NULL) return ;
        inorder(root->left);
        
        if(pre != NULL && pre->val >= root->val){
            if(first == NULL) //这里经过vs2013验证 如果没有对first初始化, 那么其值为NULL
                first = pre;
            second = root;
        }
        pre = root;
        inorder(root->right);
    }
    //stack 方法:
    void recoverTree(TreeNode* root) {
        TreeNode* first = NULL;
        TreeNode* second = NULL;
        TreeNode* pre = NULL;
        TreeNode* cur = root;
        stack<TreeNode*> s;
        while(!s.empty() || cur)
        {
            if(cur)
            {   //visit curr's left subtree
                s.push(cur);
                cur = cur->left;
            }else{//done left subtree of curr Node
                cur = s.top();
                s.pop();
                //visit root and do something
                if(pre != NULL && pre->val >= cur->val){
                    if(first == NULL) 
                        first = pre;
                    second = cur;
                }
                pre = cur;
                //visit curr's right subtree
                cur = cur->right;
            }
            
        }
        int temp = first->val;
        first->val = second->val;
        second->val = temp;
        
    }
    //Morris 方法
    void recoverTree(TreeNode* root) {
        TreeNode* first = NULL;
        TreeNode* second = NULL;
        TreeNode* pre = NULL;
        TreeNode* pred = NULL;
        TreeNode* cur = root;
        while(cur != NULL)
        {
            if(pre != NULL && pre->val >= cur->val){
                if(first == NULL) 
                    first = pre;
                second = cur;
            }
            if(cur->left != NULL){
                //find cur's predecessor
                pred = cur->left;
                while(pred->right != NULL && pred->right != cur) pred = pred->right;
                if(pred->right != cur){
                    pred->right = cur;
                    cur = cur->left;
                }else{
                    pred->right = NULL;
                    //visit 
                    pre = cur;
                    cur = cur->right;
                }
            }else{//visit 
                pre = cur;
                cur = cur->right;
            }
        }
        int temp = first->val;
        first->val = second->val;
        second->val = temp;
    }
};
详解:

  • In-order traversal is really useful in BST. Following in-order traversal, we should have following order: prev.val < curr.val. If not, then we found at least one incorrectly placed node

    So the basic idea is to visit the tree with in-order traversal and search for two swapped nodes. Then swap them back.

    Now the problem is if we found an incorrect pair where prev.val > curr.val, how do we know which node is the incorrect one? The answer is it depends on whether we have found incorrect node before. So What is that?

    Since we get two elements that are swapped by mistake, there must be a smaller TreeNode get a larger value and a larger TreeNode get a smaller value.
    Their value are swapped, but the incorrect smaller node is still in smaller tree and incorrect larger node is still in larger tree. So we will visit the incorrect smaller node first, and this node will be detected when we compare its value with next.val, i.e. when it is treated as prev node. The incorrect larger node will be detected when we compare its value with prev.val. We don't know if it is close or not close to incorrect smaller node, so we should continue search BST and update it if we found another incorrect node.

    Therefore if it is the first time we found an incorrect pair, the prev node must be the first incorrect node.
    If it is not the first time we found an incorrect pair, the curr node must be the second incorrect node, though
    we may have corner case that two incorrect nodes are in same pair.

    Recursive in-order traversal based on above idea:

    public void recoverTree(TreeNode root) {
        //use inorder traversal to detect incorrect node
        
        inOrder(root);
        
    
        int temp = first.val;
        first.val = second.val;
        second.val = temp;
    }
    
    TreeNode prev = null;
    TreeNode first = null;
    TreeNode second = null;
    
    public void inOrder(TreeNode root){
        if(root == null) return;
        //search left tree
        inOrder(root.left);
        
        //in inorder traversal of BST, prev should always have smaller value than current value
        if(prev != null && prev.val >= root.val){
            //incorrect smaller node is always found as prev node
            if(first == null) first = prev;
          //incorrect larger node is always found as curr(root) node
            second = root;
        }
        
        
        //update prev node
        prev = root;
    
        //search right tree
        inOrder(root.right);
    }
    

    iterative in-order traversal based on above idea:

    public void recoverTree(TreeNode root) {
        TreeNode first = null;
        TreeNode second = null;
        
        TreeNode curr = root;
        TreeNode prev = null;
        
        Stack<TreeNode> stack = new Stack<TreeNode>();
        
        while(!stack.isEmpty() ||  curr != null){
            if(curr != null){
                //visit curr's left subtree
                stack.push(curr);
                curr = curr.left;
            }else{
                //done left subtree of curr Node
                curr = stack.pop();
                
                //compare curr.val with prev.val if we have one
                if(prev != null && curr.val <= prev.val){
                    //incorrect smaller node is always found as prev node
                    if(first == null) first = prev;
                    //incorrect larger node is always found as curr node
                    second = curr;         
                }  
                
                //visit curr's right subtree
                prev = curr;
                curr = curr.right;
            }
        }
        
        //recover swapped nodes
        int temp = first.val;
        first.val = second.val;
        second.val = temp;
    }
    

    Both recursive and iterative will occupy O(n) space in worst case, in which the tree is like a LinkedList

    To reduce the space to constant space, we have to use Morris-traversal.

    Morris-traversal is similar to recursive/iterative traversal, but we need to modify the tree structure during the
    traversal. before we visiting the left tree of a root, we will build a back-edge between rightmost node in left tree and the root. So we can go back to the root node after we are done with the left tree. Then we locate the rightmost node in left subtree again, cut the back-edge, recover the tree structure and start visit right subtree. The detection of two incorrect TreeNodes is similar to iterative/recursive in-order traversal.
    We don't use extra data structure here, so the space complexity is reduced to O(1) and the time complexity will be O(n)

    Morris-traversal based on above description:

    public void recoverTree(TreeNode root) {
    	//Morris-traversal
    	
        TreeNode first = null;
        TreeNode second = null;
        
        TreeNode pred = null; //rightmost node in left tree
        TreeNode prev = null; 
        
        TreeNode curr = root;
        
        while(curr != null){
            //for each node, we compare it with prev node as we did in in-order-traversal
            if(prev != null && curr.val <= prev.val){
                if(first == null) first = prev;
                second = curr;
            }
            
            if(curr.left != null){
                //got left tree, then let's locate its rightmost node in left tree
                pred = curr.left;
                //we may have visited the left tree before, and connect the rightmost node with curr node (root node)
                while(pred.right != null && pred.right != curr){
                    pred = pred.right;
                }
                
                if(pred.right == curr){
                    //if this left tree has been visited before, then we are done with it
                    //cut the connection with currNode and start visit curr's right tree
                    pred.right = null;
                    prev = curr;
                    curr = curr.right;
                }else{
                    //if this left tree has not been visited before, then we create a back edge from rightmost node
                    // to curr node, so we can return to the start point after done the left tree
                    pred.right = curr;
                    curr = curr.left;
                }
                
            }else{
                //no left tree, then just visit its right tree
                prev = curr;
                curr = curr.right;
            }
        }
        
        int temp = first.val;
        first.val = second.val;
        second.val = temp;
    }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值