99. Recover Binary Search Tree
Total Accepted: 50739 Total Submissions: 192395 Difficulty: Hard
Two elements of a binary search tree (BST) are swapped by mistake.
Recover the tree without changing its structure.
Note:
confused what "{1,#,2,3}"
means? > read more on how binary tree is serialized on OJ.
【分析】
题意:一个二叉搜索树(BST)的两个节点数值被交换(swapped)而发生错误,要求恢复二叉搜索树而不改变它的原有结构,附加要求:使用常量空间。
这个题是对【LeetCode】98.Validate Binary Search Tree的加深。我们知道:二叉搜索树的中序遍历是一个升序序列,我们只要对一个二叉树进行中序遍历,然后检验其序列是否为升序便可以判断该二叉树是否为合法的二叉搜索树,同理,本题中二叉搜索树的两个节点数值发生了互换,那么正常的升序序列必然会出现局部降序,我们只要找到局部降序点并将对应数据进行交换,便可恢复二叉搜索树。关于局部降序有几种情况必须分析清楚:
1、特殊情况:输入二叉搜索树根节点为空,或者只有一个节点,直接返回,无处理;
2、输入二叉搜索树只有两个节点,直接互换,返回;
3、输入二叉搜索树的节点数>=3,中序遍历序列出现局部降序的位置有两种情况:a.相邻,如[3,6,4];b.不相邻,如[6,4,5,3],两种情况,最明显的区别就是前者只会出现一次降序点(4<6);后者则有两个降序点(4<6,3<5),且第一个降序点前一个数6是错误位置,后一个降序点后一个数3是错误位置,一定要注意。
【解法一】
根据上述分析,一个非常的直观的思路就是对二叉树进行中序遍历,用一个动态空间存储中序遍历结果,然后对这个序列进行检验,找出局部降序点的位置并交换,然后再对二叉搜索树进行一次中序遍历并利用恢复后的序列对各个节点进行赋值。这样做思路很简单,但空间复杂度O(n),时间复杂度O(n),由于使用了非常量空间存储中序遍历结果,因此并不契合题意,不过,作为一种可行的解法还是可以的,这里我也给出代码。
class Solution { private: int count=0;//恢复二叉树时的数组下标记录变量 public: void recoverTree(TreeNode* root) { vector<int> res;//记录中序遍历结果 if(root==NULL)return; inoderTraversal(root,res);//中序遍历 int firstdp=0;//记录第一次出现降序的前一个数据的下标 int temp=0; bool flag=false;//标记第一个降序点出现 //只有一个节点和两个节点的处理 if(res.size()==1)return; if(res.size()==2) { temp=res[0]; res[0]=res[1]; res[1]=temp; insert(root,res);//通过中序遍历重新为二叉树赋值 return; } //遍历中序遍历序列,找出局部降序点并替换数据 for(int i=1;i<res.size();i++) { if(res[i]<res[i-1]&&!flag) {flag=true;firstdp=i-1;} else if(res[i]<res[i-1]) { temp=res[i]; res[i]=res[firstdp]; res[firstdp]=temp; insert(root,res);//通过中序遍历重新为二叉树赋值 return; } } //如果只有一个局部降序点,即被交换的两个数据相邻 temp=res[firstdp]; res[firstdp]=res[firstdp+1]; res[firstdp+1]=temp; insert(root,res);//通过中序遍历重新为二叉树赋值 } //中序遍历 void inoderTraversal(TreeNode* root,vector<int>& res) { if(root==NULL)return; inoderTraversal(root->left,res); res.push_back(root->val); inoderTraversal(root->right,res); } void insert(TreeNode* root,vector<int>& res) { if(root==NULL)return; insert(root->left,res); root->val=res[count++]; insert(root->right,res); } };
【解法二】
设置两个指针分别指向中序遍历中出现局部降序的节点(注意,分析3中的一前,二后问题),设置一个指针指向当前节点的前一个节点,在中序遍历中比较前一节点和当前节点的数值,从而判断是否为局部降序,最后,将两个局部降序指针指向的节点的数值交换,即可完成搜索二叉树的恢复。
class Solution { public: void recoverTree(TreeNode* root) { int firstdp=0; if(root==NULL||(root->left==NULL&&root->right==NULL)) return; inoderTraversal(root); if(firstNode!=NULL&&secondNode!=NULL) { int temp=firstNode->val; firstNode->val=secondNode->val; secondNode->val=temp; } } void inoderTraversal(TreeNode* root) { if(root==NULL)return; inoderTraversal(root->left); if(pre!=NULL&&pre->val>root->val&&!flag) { firstNode=pre; flag=true; secondNode=root; } else if(pre!=NULL&&pre->val>root->val&&flag) { secondNode=root; } pre=root; inoderTraversal(root->right); } private: TreeNode *firstNode=NULL; TreeNode *secondNode=NULL; TreeNode *pre=NULL; bool flag=false; };