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?
Naive Way:O(1)的做法我实在是想不出来,明明inorder traversal 就要O(n) space了。
这是O(n) space 的做法,这样做比较无脑,任何错误都可以纠正。
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public void recoverTree(TreeNode root) {
List<Integer> vals = new ArrayList<Integer>();
List<TreeNode> nodes = new ArrayList<TreeNode>();
inorder(root, nodes, vals);
Collections.sort(vals);
for(int i = 0;i < nodes.size();i++)
nodes.get(i).val = vals.get(i);
}
private void inorder(TreeNode root, List<TreeNode> nodes, List<Integer> vals){
if(root==null) return;
inorder(root.left, nodes, vals);
nodes.add(root);
vals.add(root.val);
inorder(root.right, nodes, vals);
}
}
Improved Way: 原来还有一个叫Morris Threading Traversal 的东西。这里有一个blog,是一个中国人专门写Morris Traversal的,http://www.cnblogs.com/AnnieKim/archive/2013/06/15/morristraversal.html,非常有用。线索二叉树。
针对这道题,Morris Traversal 解决了用O(1)的space遍历BST,但是找到错误交换的两个点依然是一个问题。肯定是在Morris Traversal的算法上修改。先把Morris Traversal的算法列出来。
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public void morrisTraversal(TreeNode root){
TreeNode temp = null;
while(root!=null){
if(root.left!=null){
// connect threading for root
temp = root.left;
while(temp.right!=null && temp.right != root)
temp = temp.right;
// the threading already exists
if(temp.right!=null){
temp.right = null;
System.out.println(root.val);
root = root.right;
}else{
// construct the threading
temp.right = root;
root = root.left;
}
}else{
System.out.println(root.val);
root = root.right;
}
}
}
两个加粗部分对应遍历时的输出顺序,所以找出错误交换的节点也应该是在这里修改。如何修改?因为MorrisTraversal得到的应该是升序的,所以前一个大于后一个说明前一个一定是错的,为什么不是后一个是错的呢?因为后一个比前一个小说明后一个应该被先遍历到,前一个节点至少应该是后一个节点那么大小。而一旦找到了第一个错误的节点,第二次再遇到前一个大于后一个的时候,错误的就是后一个了。为什么呢?因为我们知道了第一个错误的节点是value大于它本来的value,所以我们现在要找的就是value小于它本来value的节点,因为这个节点本来应该是第一个错误的节点所在的位置。
而当出错的是连续两个节点时,可能只检测到一个非升序情况,因为连着的这两个是错的。这时要暂时另第二个错误节点等于第一个后面的节点。
所以要做的就是把
System.out.println(root.val);
替换成
if(pre!=null && pre.val > root.val){
if(first==null){first = pre;second = root;}
else{second = root;}
}
pre = root;
这里非常好理解,因为原来是按照System.out.println(root.val)的顺序输出的,也就是说该位置得到的每一次root值都是排好序的,那么将上一次的root作为这一次的pre就是按照顺序的pre和root,如果二者大小关系不对,可知错误节点在此处。
public void recoverTree(TreeNode root) {
TreeNode pre = null;
TreeNode first = null, second = null;
// Morris Traversal
TreeNode temp = null;
while(root!=null){
if(root.left!=null){
// connect threading for root
temp = root.left;
while(temp.right!=null && temp.right != root)
temp = temp.right;
// the threading already exists
if(temp.right!=null){
if(pre!=null && pre.val > root.val){
if(first==null){first = pre;second = root;}
else{second = root;}
}
pre = root;
temp.right = null;
root = root.right;
}else{
// construct the threading
temp.right = root;
root = root.left;
}
}else{
if(pre!=null && pre.val > root.val){
if(first==null){first = pre;second = root;}
else{second = root;}
}
pre = root;
root = root.right;
}
}
// swap two node values;
if(first!= null && second != null){
int t = first.val;
first.val = second.val;
second.val = t;
}
}
No comments:
Post a Comment