leetcode 99. 恢复二叉搜索树

题目

https://leetcode-cn.com/problems/recover-binary-search-tree/
leetcode 99. 恢复二叉搜索树_第1张图片

第一次尝试

思路就是对当前根节点,找到其左右子树里面第一个不符合定义的结点
如果这个结点存在,交换他们的值,然后再对当前结点重复递归
如果这个结点不存在,则递归左右子树
这个方法通过重复交换能解决一般的树,但是[3,null,2,null,1]就不行了
第一次交换会得到2,3,1.第二次交换会得到1,3 ,2,第三次交换找到不符合定义的结点是2,但是由于当前根节点是1所以交换后变成2,3,1,又变回来了
这个方法行不通

class Solution {
public:
    void recoverTree(TreeNode* root) {
        if(!root)return;
        //假设有一个结点node,如果它存在,就交换并结束,如果它不存在,就递归左右子树交换
        TreeNode* node=func(INT_MIN,INT_MAX,root);
        if(node){
            //一次交换后,可能仍然不是符合规则的树,重复交换直到符合规则
            swap(root->val,node->val);
            recoverTree(root);
            return ;
        }
        else{
            recoverTree(root->left);
            recoverTree(root->right);
        }
        
    }
    //函数功能:给定一个结点,分别在其左右子树中查找不符合二叉搜索树定义的结点并返回
    //1.空结点就返回空
    //2.返回符合条件的结点
    //3.返回左右子树中存在的结点
    TreeNode* func(int low,int high,TreeNode* root){
        if(!root)return NULL;
        if(root->val<low||root->val>high)return root;
        TreeNode* Lnode=func(low,root->val,root->left);
        TreeNode* Rnode=func(root->val,high,root->right);
        return Lnode?Lnode:Rnode;
    }
};

第二次尝试 暴力法

利用二叉搜索树中序遍历是升序的性质,但是效率不是很高

class Solution {
public:
    int i;
    vector<int>vt;
    void recoverTree(TreeNode* root) {
        i=0;
        func1(root);
        sort(vt.begin(),vt.end());
        func2(root);
    }
    void func1(TreeNode* root){
        if(!root)return;
        func1(root->left);
        vt.push_back(root->val);
        func1(root->right);
    }
    void func2(TreeNode* root){
        if(!root)return;
        func2(root->left);
        root->val=vt[i++];
        func2(root->right);
    }
};

leetcode 99. 恢复二叉搜索树_第2张图片

讨论区

优化的中序遍历

考虑到题目中说了只有两个结点错误的交换,那么中序遍历中只需要交换一对结点就能恢复二叉树了
在中序序列 6, 3, 4, 5, 2 中,6 3 和 5 2分别是第一对相邻的逆序对和最后一对相邻的逆序对,那么我们只要在遍历过程中

  1. 标记第一个逆序对的第一个结点
  2. 使用pre标记前一个结点,与当前结点判断,当符合逆序对时,更新第二个结点

逆序对就是指在数组中 a [ i ] > a [ j ] a[i]>a[j] a[i]>a[j] i < j ii<j的两个数
按照这种方法中序遍历,第一个结点就是6,第二个结点就是2,交换6和2就得到
2,3,4,5,6 正确的序列了

class Solution {
public:
    //t1和t2是用于交换的结点值,pre是中序遍历过程中用来比较的结点
    TreeNode* t1=NULL;
    TreeNode* t2=NULL;
    TreeNode* pre=NULL;
    void recoverTree(TreeNode* root) {
        func1(root);
        swap(t1->val,t2->val);
    }
    void func1(TreeNode* root){
        if(!root)return;
        func1(root->left);
        //如果前一个结点存在并且与当前结点构成逆序对
        if(pre&&pre->val>root->val){
            if(!t1)t1=pre;  //记录第一个要交换的结点
            t2=root;	//不断更新第二个要交换的结点
        }
        pre=root;	//往后寻找逆序对
        func1(root->right);
    }
};

空间复杂度是 O ( l o g n ) O(logn) O(logn)
leetcode 99. 恢复二叉搜索树_第3张图片

莫里斯解法

这个解法的空间复杂度是 O ( 1 ) O(1) O(1)的,但是我不太看得懂,就没写了额
贴一篇很详细的文章 https://www.cnblogs.com/AnnieKim/archive/2013/06/15/morristraversal.html

你可能感兴趣的:(leetcode)