【LeetCode #99】Recover Binary Search Tree

原题链接: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. 如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点。

    • 如果找到的当前节点的直接前驱的右孩子为空,则将此直接前驱节点的右孩子设置为当前节点。将当前节点更新为当前节点的左孩子。

    • 如果找到的当前节点的直接前驱节点的右孩子为当前节点,将它的右孩子重新设为空(恢复树的形状)。输出当前节点。然后将当前节点更新为当前节点的右孩子。

  3. 重复以上1、2 直到当前节点为空。

举一个具体例子:
例如1,4,3,2,5,6

  1. 当我们读到4的时候,发现是正序的,不做处理

  2. 但是遇到3时,发现逆序,将4存为第一个错误节点,3存为第二个错误节点

  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 解题报告

posted @ 2015-06-17 15:40  lanqiu5ge  阅读(138)  评论(0编辑  收藏  举报