leetcode-Recover Binary Search Tree

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?

confused what "{1,#,2,3}" means? > read more on how binary tree is serialized on OJ.

题目说一个二叉搜索树的两个节点被错误交换,要求不改变树的结构恢复正常的二叉搜索树。

解法一:

O(n)的空间复杂度的解决办法是比较容易的,我们可以把二叉搜索树按中序遍历赋值到数组中,然后直接扫描数组元素记录相邻两个数字逆序的位置,相邻两个数字逆序的对数可能有一对,也可能有两对。假设元素按顺序排序是1、2、3、4、5.一对的情况,如:1、2、4、3、5,这样我们直接交换4和3的值即可。如果是两对的情况,例如1、5、3、4、2,我们查找到逆序位置的元素5和2,交换5和2即可。

但是如何根据这个信息去交换二叉树中的节点呢?我们可以存储元素值与树节点指针的映射,用STL的map容器,key为元素值,value为节点指针。

代码如下:


/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    void recoverTree(TreeNode *root) {
        vector<int> v;
        map<int, TreeNode*> mm;
        //inorder 
        stack<TreeNode*> s;
        s.push(root);
        while (!s.empty()) {
            TreeNode *tmp = s.top();
            while (tmp->left!=NULL) {
                s.push(tmp->left);
                tmp = tmp->left;
            }
            v.push_back(tmp->val);
            mm[tmp->val] = tmp;
            s.pop();
            while (tmp->right==NULL && !s.empty()) {
                tmp = s.top();
                v.push_back(tmp->val);
                mm[tmp->val] = tmp;
                s.pop();
            }
            if (tmp->right!=NULL)
                s.push(tmp->right);
        }
        int p1=-1, p2 = -1;
        for (int i=1; i<v.size(); ++i) {
            if (v[i]<v[i-1]) {
                if (p1==-1) {
                    p1 = i-1;
                } else {
                    p2 = i-1;
                }
            }
        }

        if (p2==-1) {
            swap(mm[v[p1]]->val, mm[v[p1+1]]->val);
        } else {
            swap(mm[v[p1]]->val, mm[v[p2+1]]->val);
        }
    }
private:
    void swap(int &a, int &b) {
        int tmp = a;
        a = b;
        b = tmp;
    }
};

解法二:

认真考虑二叉搜索树的性质,左子树的值比根节点小,右子树的值比根节点大,如果两个节点值被交换,有下面四种情况,一是左右子树的两个节点被交换,二是根节点与左子树一节点被交换,三是根节点与右子树一节点被交换,四是左子树或者右子树里面的两个节点被交换。

情况一时,我们可以在左子树中找比根节点值大的节点值,在右子树中找比根节点值小的节点值,然后交换找到的两个节点的值;

情况二时,我们要找到左子树中比根节点值最大的节点,然后交换根节点与找到的节点;

情况三时,我们要找到右子树中比根节点值最小的节点,然后交换根节点与找到的节点;

情况四时,我们对根节点的左右子树再递归调用这样的操作。

那么如何判断是哪种情况发生了呢?通过在左子树中找比根节点大的最大节点x,在右子树中找比根节点值小的最小节点y,如果x和y都存在,说明是x和y被交换了;如果x存在而y不存在,只能是根节点与x被交换了;类似,如果y存在而x不存在,只能是根节点与y被交换了;如果都不存在,那么是左子树或右子树里的两个节点被交换了。


/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    void recoverTree(TreeNode *root) {
        if (root==NULL)
            return;
        //找左子树孩子的最大值节点
        TreeNode *leftBiggest = root;
        find(root->left, leftBiggest, "largest"); 
        //找右子树孩子的最小值节点
        TreeNode *rightSmallest = root;
        find(root->right, rightSmallest, "smallest");
        
        // root的两个左右子孙误交换了
        if (leftBiggest!=root && rightSmallest!=root) {
            swap(leftBiggest->val, rightSmallest->val);
        } else if(leftBiggest != root) {  //右子树正常,root与leftBiggest误交换了
            swap(root->val, leftBiggest->val);
        } else if (rightSmallest != root) {  //左子树正常,root与rightSmallest误交换了
            swap(root->val, rightSmallest->val);
        } else {   // 左子树或右子树里的两个节点误交换
            recoverTree(root->left);
            recoverTree(root->right);
        }
    }
private:
    void find(TreeNode *root, TreeNode *&p, const string& relation) {
        if (root == NULL)
            return;
        if (relation=="smallest" && root->val<p->val)
            p = root;
        else if (relation=="largest" && root->val>p->val)
            p = root;
        find(root->left, p, relation);
        find(root->right, p, relation);
    }
    void swap(int &a, int &b) {
        int tmp = a;
        a = b;
        b = tmp;
    }
};





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值