题目描述:
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?
根据“BST的中序遍历是有序的”这一特征,就有如下两种情况值得考虑:
1 如果出现交换的两个数是相邻的,
如:1 2 3 4 5 6 7,交换 2和3,得到 1 3 2 4 5 6 7 ,也就是说在交换之后形成的二叉树中会出现一对“反常”的数,如本例中的(3,2)
2 如果出现交换的数不是相邻的,
如: 1 2 3 4 5 6 7 ,交换 2 和5 ,得到 1 5 3 4 2 6 7 ,也就是说在交换之后会有两对“反常”的数,如本例中的(5,3)和(4,2).
思路 1:使用中序遍历的非递归实现,利用栈可得到:
void recoverTree(TreeNode *root) {
if(root == NULL)return;
stack< TreeNode *> s1;
TreeNode *node1 = NULL;
TreeNode *node2 = NULL;
TreeNode *pre = NULL;
while(root || !s1.empty())
{
while(root)
{
s1.push(root);
root = root->left;
continue;
}
root = s1.top();
s1.pop();
if(pre && ( root->val < pre->val) )
{
if(node1 == NULL) node1 = pre; //当前值比后面的值大,那么pre一定是node1,root有可能是node2。因此必须将node2的值保存下来,如果再次遇到pre>root的情况的时候,那么现在的root就一定是node2.
//如果没有再次遇到pre>root的话,那么说明是相邻的两个节点出现了交换。
node2 = root;
}
pre = root;
root = root->right;
}
swap(node1->val,node2->val);
return;
}
关于上述两种情况的说明,已经在程序中注释证明了。时间复杂度为O(n).
由于我每一次只需要与前驱节点进行比较,因此使用Morris遍历,可将时间复杂度降低到O(1)。关于Morris中序遍历,参见Morris中序遍历,以及线索二叉树。
2 采用中序遍历的常数空间的版本。代码如下:
void recoverTree(TreeNode *root) {
if(root == NULL)return;
TreeNode *ch1(NULL);
TreeNode *ch2(NULL);
TreeNode *pre(NULL);
while(root)
{
if(root->left == NULL)
{
if(pre && pre->val > root->val)
{
if(ch1 == NULL) ch1 = pre;
ch2 = root;
}
pre = root;
root = root->right;
continue;
}
TreeNode *temp = root->left;
for(;temp->right && temp->right != root; temp = temp->right);
if(temp->right == NULL)
{
temp->right = root;
root = root->left;
}
else
{
if(pre && pre->val > root->val)
{
if(ch1 == NULL) ch1 = pre;
ch2 = root;
}
temp->right = NULL;
pre = root;
root = root->right;
}
}
swap(ch1->val,ch2->val);
return ;
}
本题的难点应该在于,如果交换的是相邻的节点,那么只会出现一对反常的数。