原题链接: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.
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:
1
/
2 3
/
4
5
The above binary tree is serialized as “{1,2,3,#,#,4,#,#,5}”.
Solution:
O(n) 空间的解法是,开一个指针数组,中序遍历,将节点指针依次存放到数组里,然后寻找两处逆向的位置,先从前往后找第一个逆序的位置,然后从后往前找第二个逆序的位置,交换这两个指针的值。
中序遍历一般需要用到栈,空间也是O(n) 的,如何才能不使栈?
Morris 中序遍历
具体的思路,还是通过中序遍历,只不过,不需要存储每个节点,只需要存一个前驱即可。
【具体步骤】
如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。
如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点。
如果找到的当前节点的直接前驱的右孩子为空,则将此直接前驱节点的右孩子设置为当前节点。将当前节点更新为当前节点的左孩子。
如果找到的当前节点的直接前驱节点的右孩子为当前节点,将它的右孩子重新设为空(恢复树的形状)。输出当前节点。然后将当前节点更新为当前节点的右孩子。
重复以上1、2 直到当前节点为空。
举一个具体例子:
例如1,4,3,2,5,6
当我们读到4的时候,发现是正序的,不做处理
但是遇到3时,发现逆序,将4存为第一个错误节点,3存为第二个错误节点
继续往后,发现3,2又是逆序了,那么将第2个错误节点更新为2
如果是这样的序列:1,4,3,5,6同上,得到逆序的两个节点为4和3。
这里我们补充一下,为什么要替换第二个节点而不是第一个节点?
e.g. The correct BST is below:
inorder traversal is : 1 3 4 6 7 8 10 13 14
Wrong order: 1 3 8 6 7 4 10 13 14
FIND: 8 6
Then we find: 7 4
8, 6 是错误的序列, 但是,7,4也是错误的序列。
因为8,6前面的序列是正确的,所以8,6一定是后面的序列交换来的。
而后面的是比较大的数字,也就是说8一定是被交换过来的。而7,4
中也应该是小的数字4是前面交换过来的。
用反证法来证明:
假设:6是后面交换过来的
推论: 那么8比6还大,那么8应该也是后面交换来的,
这样起码有3个错误的数字了
而题目是2个错误的数字,得证,只应该是8是交换过来的。
结论就是:我们需要交换的是:8, 4.
代码:
//二叉树节点结构
struct TreeNode{
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x):val(x),left(NULL),right(NULL){}
};
//核心函数
void recoverTreeByMorrisInorderTraversal(TreeNode *root)
{
if(root == NULL)
return ;
TreeNode *cur = root;
TreeNode *prev = NULL;
TreeNode *firstNode = NULL;
pair<TreeNode*, TreeNode*> broken;
while (cur != NULL)
{
if(cur->left == NULL)
{
detect(broken, firstNode, cur);
firstNode = cur;
cur = cur->right;
}
else
{
prev = cur->left;
while(prev->right != NULL && prev->right != cur)
prev = prev->right;
if(prev->right == NULL)
{
prev->right = cur;
cur = cur->left;
}
else
{
detect(broken, firstNode, cur);
prev->right = NULL;
firstNode = cur;
cur = cur->right;
}
}
}
swap(broken.first->val, broken.second->val);
}
void detect(pair<TreeNode*, TreeNode*>& broken, TreeNode* prev, TreeNode* cur)
{
if(prev != NULL && prev->val > cur->val)
{
if(broken.first == NULL)
broken.first = prev;
//只是每次替换第二个节点的值
broken.second = cur;
}
}
【参考】
Morris Traversal方法遍历二叉树
九章算法-LeetCode: Recover Binary Search Tree 解题报告