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

  • 相关阅读:
    Windows——bat中的路径和工具栏运行bat的坑
    KBE——查询
    KBE实践——登录案例
    KBE_运作流程
    KBE_创建项目和基本常识
    python_面向对象
    程序员常见面试之 数据库 知识点小结(三)
    C#编程总结
    人人必知的10个 jQuery 小技巧
    Javascript生成二维码(QR)
  • 原文地址:https://www.cnblogs.com/lanqiu5ge/p/9472219.html
Copyright © 2020-2023  润新知