1. 题目
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?
OJ’s Binary Tree Serialization:
The serialization of a binary tree follows a level order traversal, where ‘#’ signifies a path terminator where no node exists below.Here’s an example:
![]()
The above binary tree is serialized as “{1,2,3,#,#,4,#,#,5}”.
2.解题思路
一颗二叉搜索树里面的两个节点的值被互相交换了,要求找到这两个节点进行值交换复原这颗二叉树。
既然是二叉树,那必然要用到它的性质了:左节点的值小于自己的值,自己的值小于右节点的值。
基于这个性质,二叉树的中序遍历是从小到大有序的。交换两个节点势必会破坏这种有序性,较大的节点被换到了小节点处,较小的节点被换到了大节点处。
因此,中序遍历这棵树,如果某一个值小于前一个输出的值,那么前一个输出的值必然是交换的节点中的大节点;继续遍历,如果再次出现这种情况,那么当前这个值比如是交换的节点中的小节点。也就是说,交换后,序列中出现了两对逆序(当然,如果仅仅把两个相邻的交换,那么只有一对)。
如果有两对逆序,第一对中的较大值对应交换节点中的大节点;第二对中的较小值对应交换节点中的小节点。只有一对相当于把两对合并。
非递归C++代码如下:
/**
* 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) {
stack<TreeNode *> s;
TreeNode *Front;
TreeNode *FirstError=NULL;
TreeNode *SecondError=NULL;
int a=0,b=0x80000000;
while(root||!s.empty())
{
if (root)
{
s.push(root);
root=root->left;
}
else
{
root=s.top();
s.pop();
a=b;
b=root->val;
if (b<a)
{
if (FirstError)
{
SecondError=root;
break;
}
else
{
FirstError=Front;
SecondError=root;
}
}
Front=root;
root=root->right;
}
}
a=FirstError->val;
FirstError->val=SecondError->val;
SecondError->val=a;
}
};
非递归方法需要使用一个栈来辅助,栈的大小需要大于二叉树的最大深度,和常数空间的要求不甚符合,又实现了递归方法:
/**
* 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) {
Pre=NULL;
First=NULL;
traversal(root);
int temp = First->val;
First->val = Second->val;
Second->val = temp;
}
void traversal(TreeNode *root)
{
if (!root)
return ;
traversal(root->left);
if (Pre!=NULL&&root->val<Pre->val)
{
if (!First)
First = Pre;
Second = root;
}
Pre = root;
traversal(root->right);
}
TreeNode *First,*Second,*Pre;
};
细想一下,其实递归的时候不断调用函数,压栈的过程也是需要空间的,而且也是O(logn),也不符合常数空间。
怎么办呢?
我搜了搜,发现了一种O(1)的二叉树遍历方法,Morris算法,感兴趣的可以了解一下,在这里我就不具体说了。
void recoverTree(struct TreeNode *root) {
struct TreeNode *First=NULL;
struct TreeNode *Second=NULL;
struct TreeNode *LastRead=NULL;
struct TreeNode *Pre = NULL;
while (root!= NULL)
{
if (root->left == NULL)
{
if (LastRead!=NULL)
{
if (LastRead->val>root->val)
{
if (First==NULL)
First=LastRead;
Second=root;
}
}
LastRead=root;
root = root->right;
}
else
{
Pre = root->left;
while (Pre->right != NULL && Pre->right != root)
Pre = Pre->right;
if (Pre->right == NULL)
{
Pre->right = root;
root = root->left;
}
else
{
Pre->right = NULL;
if (LastRead!=NULL)
{
if (LastRead->val>root->val)
{
if (First==NULL)
First=LastRead;
Second=root;
}
}
LastRead=root;
root = root->right;
}
}
}
int temp = First->val;
First->val = Second->val;
Second->val = temp;
}