Thursday, October 2, 2014

Recover Binary Search Tree

from codeganker
这道题是要求恢复一颗有两个元素调换错了的二叉查找树。一开始拿到可能会觉得比较复杂,其实观察出规律了就比较简单。主要还是利用二叉查找树的主要性质, 就是中序遍历是有序的性质。那么如果其中有元素被调换了,意味着中序遍历中必然出现违背有序的情况。那么会出现几次呢?有两种情况,如果是中序遍历相邻的 两个元素被调换了,很容易想到就只需会出现一次违反情况,只需要把这个两个节点记录下来最后调换值就可以;如果是不相邻的两个元素被调换了,举个例子很容 易可以发现,会发生两次逆序的情况,那么这时候需要调换的元素应该是第一次逆序前面的元素,和第二次逆序后面的元素。比如1234567,1和5调换了, 会得到5234167,逆序发生在52和41,我们需要把4和1调过来,那么就是52的第一个元素,41的第二个元素调换即可。
搞清楚了规律就容易实现了,中序遍历寻找逆序情况,调换的第一个元素,永远是第一个逆序的第一个元素,而调换的第二个元素如果只有一次逆序,则是那一次的 后一个,如果有两次逆序则是第二次的后一个。算法只需要一次中序遍历,所以时间复杂度是O(n),空间是栈大小O(logn)。代码如下:

class Solution {
public:
    void recoverTree(TreeNode *root) {
        if(!root) return;
        TreeNode*pre=NULL;
        vector<TreeNode*> ret;
        inorder(root,pre,ret);
        swap(ret[0]->val,ret[1]->val);
    }
    void inorder(TreeNode*root,TreeNode*&pre,vector<TreeNode*>&ret){
        if(!root) return;
        inorder(root->left,pre,ret);
        if(pre&&pre->val>root->val){
            if(ret.size()==0){
                ret.push_back(pre);ret.push_back(root);
            }
            else{
                ret[1]=root;
            }
        }
        pre=root;
        inorder(root->right,pre,ret);
    }
};

// from anniekim, the same idea, only a  different flavor

void recoverTree(TreeNode *root) {
    TreeNode *prev = NULL, *first = NULL, *second = NULL;
    recoverTreeRe(root, prev, first, second);
    swap(first->val, second->val);
}
void recoverTreeRe(TreeNode *curNode, TreeNode *&preNode, TreeNode *&first, TreeNode *&second) {
    if (curNode == NULL) return;
    recoverTreeRe(curNode->left, preNode, first, second);
    if (preNode && preNode->val > curNode->val) {
        if (first == NULL)
            first = preNode;
        second = curNode;
    }
    preNode = curNode;
    recoverTreeRe(curNode->right, preNode, first, second);
}


No comments:

Post a Comment