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。

posted @ 2014-04-07 16:20  flowerkzj  阅读(149)  评论(0编辑  收藏  举报