• Leetcode OJ: 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?
    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}".

    大意就是恢复有两个位置交换了的二叉排序树。

    思路大概就是用中序遍历,然后把顺序不对的两个数找出来。这里有两个问题。

    1. 怎么找出两个被错误交换了的节点?

    2. 题目要求最好用常量空间复杂度完成,但常见的中序遍历都是需要栈实现的,怎么做到? 

    问题1,只需要判断上一次访问的节点与当前节点的大小关系即可,第一个出现错误的是上一次访问节点(保证第一个出现的只记录一次),第二个出现错误的是当前节点。

    问题2,就有点复杂了,我们先展示下用非递归和栈实现的代码:

     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         if (NULL == root)
    14             return;
    15         stack<TreeNode*> nodes;
    16         TreeNode *p = root;
    17         TreeNode *pre = NULL, *first = NULL, *second = NULL;
    18         while (p || !nodes.empty()) {
    19             while (p) {
    20                 nodes.push(p);
    21                 p = p->left;
    22             }
    23             if (!nodes.empty()) {
    24                 p = nodes.top();
    25                 nodes.pop();
    26                 if (pre && pre->val > p->val) {
    27                     // second有可能是p
    28                     second = p;
    29                     if (!first) // first一定是pre
    30                         first = pre;
    31                     else // 当first非空,表明second一定是p
    32                         break;
    33                 }
    34                 pre = p;
    35                 p = p->right;
    36             }
    37         }
    38         // 当所有节点遍历了,second就确定了
    39         swap(first->val, second->val);
    40     }
    41 };

    这里的判断条件需要认真考虑,pre->val > p->val的情况有可能出现1次(相邻时)或者2次,而first只取第一次出现这种情况时的pre,而second只取最后一次出现这种情况的p。

    这是常规的中序遍历+判断条件完成了,可是怎么做到常量空间复杂度?
    说实话,LZ没想出来,参考http://www.cnblogs.com/TenosDoIt/p/3445682.html才知道方法。
    也是中序遍历,借助线索二叉树的思想,利用空叶子节点作为索引,这里只需要用空右叶子节点。算法为:
    1. 当前节点的左子节点为空时,判断是否出现pre->val > p->val的情况,判断完了pre = p,p = p->right
    2. 当前节点的左子节点不为空时,找到左子树的最右端非空节点:
      a) 当此节点不是当前节点(未被访问)时,则令此节点的右子节点指向当前节点
      b) 否则把此节点的对应父节点的右子节点置空(还原),判断当前节点是否出现pre->val > p->val的情况,判断完了pre = p, p = p->right
    代码如下:
     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         if (NULL == root)
    14             return;
    15         TreeNode *p = root;
    16         TreeNode *pre = NULL, *first = NULL, *second = NULL;
    17         while (p) {
    18             if (p->left) {
    19                 TreeNode* tmp = p->left;
    20                 // 找出最右端右子节点不为空且不等于p的
    21                 while (tmp->right != NULL && tmp->right != p) 
    22                     tmp = tmp->right;
    23                 if (tmp->right != p) { // 右子节点为空
    24                     tmp->right = p;
    25                     p = p->left;
    26                 } else {  // 右子节点等于p
    27                     if (pre->val > p->val) {
    28                         if (!first)
    29                             first = pre;
    30                         second = p;
    31                     }
    32                     // 还原
    33                     tmp->right = NULL;
    34                     pre = p;
    35                     p = p->right;
    36                 }
    37             } else {
    38                 if (pre && pre->val > p->val) {
    39                     if (!first)
    40                         first = pre;
    41                     second = p;
    42                 }
    43                 pre = p;
    44                 p = p->right;
    45             }
    46         }
    47         swap(first->val, second->val);
    48     }
    49 };

    对于这一方法,还原很重要,因为改变了原来的结构,因此也不能像之前说的方法那样可以提前break。

  • 相关阅读:
    扁平化职能管理三部曲
    [转载]持续交付和DevOps的前世今生
    敏捷项目管理工具-百度效率云
    敏捷项目管理:基础知识与应用实务
    第8章 “敏捷+”创新创业模式
    第7章 "敏捷+"项目管理
    第6章 迭代循环与项目结束
    第5章 发布循环
    第4章 立项与项目启动
    Windows 2003 + IIS6.0 相关 401.1 或 401.2 等问题解决
  • 原文地址:https://www.cnblogs.com/flowerkzj/p/3650284.html
Copyright © 2020-2023  润新知