• LeetCode:Recover Binary Search Tree


    其他LeetCode题目欢迎访问:LeetCode结题报告索引

    题目链接

    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?

    分析:首先我们最直观的的想法是中序遍历得到中序序列,平衡二叉树的中序序列是非递减有序的。那么问题就转化成了在非递减有序序列中交换了两个数的位置,找出这两个数并恢复有序序列,这个问题可以通过遍历一遍有序序列分下面2步完成:

    1. 首先找到第一个错误的数first,即第一个比它后缀要大的数
    2. 然后要找到第一个错误的数应该放置的位置(这就是第二个错误的数),即要找到第一个比first大的数的前驱,这个前驱就是第一个错误的数应该放的位置,也就是第二个错误的数。(注意一个特殊情况{0,1},first为1,没有找到比first大的数,这是second就是最后一个数0)

    算法1:我们可以用递归中序遍历(或者使用栈的非递归中序遍历)来实现,但是这样空间复杂度都是O(n)。下面代码是递归中序遍历,可以通过oj

     1 /**
     2  * Definition for binary tree
     3  * struct TreeNode {
     4  * int val;
     5  * TreeNode *left;
     6  * TreeNode *right;
     7  * TreeNode(int x) : val(x), left(NULL), right(NULL) {}
     8  * };
     9  */
    10 class Solution {
    11 public:
    12     void recoverTree(TreeNode *root) {
    13         // IMPORTANT: Please reset any member data you declared, as
    14         // the same Solution instance will be reused for each test case.
    15         TreeNode *pre = NULL, *first = NULL, *second = NULL;
    16         inorder(root, pre, first, second);
    17         if(first != NULL)
    18         {
    19             if(second == NULL)second = pre;//树{0,1}就可能出现这种情况
    20             int tmp = first->val;
    21             first->val = second->val;
    22             second->val = tmp;
    23         }
    24     }
    25     //pre是中序序列中当前节点的前驱,first、second分别是要找的两个乱序节点
    26     void inorder(TreeNode *root, TreeNode* &pre, TreeNode* &first, TreeNode* &second)
    27     {
    28         if(root == NULL)return;
    29         if(root->left)inorder(root->left, pre, first, second);
    30         if(pre != NULL)
    31         {
    32             if(first == NULL && root->val < pre->val)
    33                 first = pre;
    34             else if(first && root->val > first->val)
    35                 {second = pre; return;}//两个错误位置都找到就退出
    36         }
    37         pre = root;
    38         if(root->right)inorder(root->right, pre, first, second);
    39     }
    40 };
    View Code

    算法2:为了满足O(1)空间复杂度,我们就要使用非递归且不使用栈的中序遍历算法,在leetcode另一个题目Binary Tree Inorder Traversal中,我们提到了Morris Traversal中序遍历算法,它既没有递归,也没有使用栈,而是用了线索二叉树的思想,用闲置的右节点指向中序序列中该节点的后缀,遍历后再恢复树的原始指针。其主要算法步骤如下:

    重复以下1、2直到当前节点为空。                                                                                                                                                            本文地址

    1. 如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。

    2. 如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点(即当前节点的左子树的最右节点)。

       a) 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点(利用这个空的右孩子指向它的后缀)。当前节点更新为当前节点的左孩子。

       b) 如果前驱节点的右孩子为当前节点,将它的右孩子重新设为空(恢复树的形状)。输出当前节点。当前节点更新为当前节点的右孩子。

    只要在上述遍历算法上加几行代码(红色代码)判断元素是否有序的代码就可以找出乱序的两个节点(同样也是利用上面的思想分别找到第一第二个乱序数)

    需要注意的是:不能像上面的算法1那样,找到两个错误的数就退出循环。因为Morris Traversal算法破坏了原来的树的结构,需要整个循环都运行完成恢复树的结构。不然的话,就如本文第一条评论中那样,oj会出现TLE错误

     1 /**
     2  * Definition for binary tree
     3  * struct TreeNode {
     4  *     int val;
     5  *     TreeNode *left;
     6  *     TreeNode *right;
     7  *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
     8  * };
     9  */
    10 class Solution {
    11 public:
    12     void recoverTree(TreeNode *root) {
    13         // IMPORTANT: Please reset any member data you declared, as
    14         // the same Solution instance will be reused for each test case.
    15         //first,second 分别指向两个错误的节点,parent保存中序访问中当前节点的前驱
    16           TreeNode *first = NULL, *second = NULL, *parent = NULL;
    17           TreeNode *current = root, *pre = NULL;
    18           while(current != NULL)
    19           {                 
    20                 if(current->left == NULL)
    21                 {
    22                       if(parent != NULL)
    23                       {
    24                           if(first == NULL && current->val < parent->val)
    25                             first = parent;
    26                           else if(first && !second && current->val > first->val)
    27                             second = parent;
    28                       }
    29                       parent = current;
    30                       current = current->right;      
    31                 }    
    32                 else
    33                 {
    34                       /* Find the inorder predecessor of current */
    35                       pre = current->left;
    36                       while(pre->right != NULL && pre->right != current)
    37                         pre = pre->right;
    38                         
    39                       if(pre->right == NULL)
    40                       {     /* Make current as right child of its inorder predecessor */
    41                             pre->right = current;
    42                             current = current->left;
    43                       }
    44                       else 
    45                       {
    46                             /* Revert the changes made in if part to restore the original 
    47                             tree i.e., fix the right child of predecssor */ 
    48                             //这里parent肯定不等于NULL
    49                             if(first == NULL && current->val < parent->val)
    50                                 first = parent;
    51                             else if(first && !second && current->val > first->val)
    52                                 second = parent;
    53                             parent = current;
    54                             
    55                             pre->right = NULL;
    56                             current = current->right;      
    57                       } 
    58                 }
    59           } 
    60         if(first != NULL)
    61         {
    62             if(second == NULL)second = parent;//树{0,1}就可能出现这种情况
    63             int tmp = first->val;
    64             first->val = second->val;
    65             second->val = tmp;
    66         }
    67     }
    68 };

    【版权声明】转载请注明出处:http://www.cnblogs.com/TenosDoIt/p/3445682.html

  • 相关阅读:
    Java第二次作业
    JAVA学习计划
    学生选课系统
    抽奖
    Java第二次作业
    Java第六次作业修改版
    Java第六次作业
    JAVA第五次作业
    Java第四次作业
    JAVA第三次作业
  • 原文地址:https://www.cnblogs.com/TenosDoIt/p/3445682.html
Copyright © 2020-2023  润新知