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?
第三遍:
1 public class Solution { 2 TreeNode previous; 3 TreeNode first; 4 TreeNode second; 5 public void recoverTree(TreeNode root) { 6 previous = null; 7 first = null; 8 second = null; 9 inorder(root); 10 int tmp = second.val; 11 second.val = first.val; 12 first.val = tmp; 13 } 14 public void inorder(TreeNode node){ 15 if(node == null) return; 16 inorder(node.left); 17 if(previous == null || previous.val < node.val) previous = node; 18 else{ 19 if(first == null){ 20 first = previous; 21 second = node; 22 } 23 else second = node; 24 } 25 inorder(node.right); 26 } 27 }
1 /** 2 * Definition for binary tree 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Solution { 11 ArrayList<TreeNode> t; 12 TreeNode previous; 13 public void recoverTree(TreeNode root) { 14 // Start typing your Java solution below 15 // DO NOT write main() function 16 t = new ArrayList<TreeNode>(); 17 previous=null; 18 inorder(root); 19 int temp = t.get(0).val; 20 t.get(0).val = t.get(t.size()-1).val; 21 t.get(t.size()-1).val = temp; 22 } 23 public void inorder(TreeNode root) { 24 if(root==null) return; 25 inorder(root.left); 26 if(previous!=null&&previous.val>root.val) { 27 if(!t.contains(previous)) t.add(previous); 28 if(!t.contains(root)) t.add(root); 29 } 30 previous = root; 31 inorder(root.right); 32 } 33 }
The key point for this problem is when there are only two elements swapped by mistake in BST.
[1,2,3,4,5,6,7] -> [1,2,6,4,5,3,7]
We inorder traverse this tree, when we visit a node, we remember it, because it is just the previous element for the next node we visit in ascending order. when we firstly come up with a node which breaks the order, it should be the larger element which is misplaced. When we secondly come up with a node which breaks the order, it should be the smaller one which is misplaced.
The only exception is that we have that two continuous element swapped . To handle this case, when we first come up with a node which breaks the rule, we store both two elements. If we later meet another misplaced node, we replace the smaller element that we stored previously.
The inorder traverse of tree could be done iteratively. So that we do not have to make recursive call.
1 public class Solution { 2 public void recoverTree(TreeNode root) { 3 TreeNode n1 = null, n2 = null; 4 TreeNode prev = new TreeNode(Integer.MIN_VALUE); 5 TreeNode current = root; 6 Stack<TreeNode> stack = new Stack<TreeNode>(); 7 while (!stack.isEmpty() || current != null) { 8 if (current != null) { 9 stack.push(current); 10 current = current.left; 11 } else { 12 current = stack.pop(); 13 if (current.val < prev.val) { 14 if (n1 == null) { 15 n1 = prev; 16 n2 = current; 17 } 18 else n2 = current; 19 } 20 prev = current; 21 current = current.right; 22 } 23 } 24 int temp = n1.val; 25 n1.val = n2.val; 26 n2.val = temp; 27 28 } 29 }
Inorder traverse, keep the previous tree node,
Find first misplaced node by
if ( current.val < prev.val )
Node first = prev;
Find second by
if ( current.val < prev.val )
Node second = current;
After traversal, swap the values of first and second node. Only need two pointers, prev and current node. O(1) space.
但是这个解法的前提是Traverse Tree without Stack. 中序遍历如何才能不使用栈。这里就要引入一个概念, Threaded Binary Tree。So, we first create links to Inorder successor and print the data using these links, and finally revert the changes to restore original tree.
A threaded binary tree defined as follows:
"A binary tree is threaded by making all right child pointers that would normally be null point to the inorder successor of the node, and all left child pointers that would normally be null point to the inorder predecessor of the node."[1]
A threaded binary tree makes it possible to traverse the values in the binary tree via a linear traversal that is more rapid than a recursive in-order traversal. It is also possible to discover the parent of a node from a threaded binary tree, without explicit use of parent pointers or a stack, albeit slowly. This can be useful where stack space is limited, or where a stack of parent pointers is unavailable (for finding the parent pointer via DFS).
1. Initialize current as root 2. While current is not NULL If current does not have left child a) Print current’s data b) Go to the right, i.e., current = current->right Else a) Make current as right child of the rightmost node in current's left subtree b) Go to this left child, i.e., current = current->left
1 /* Function to traverse binary tree without recursion and without stack */ 2 vector<int> inorderTraversal(TreeNode *root) 3 { 4 vector<int> result; 5 TreeNode *current,*pre; 6 7 if(root == NULL) 8 return result; 9 10 current = root; 11 while(current != NULL) 12 { 13 if(current->left == NULL) 14 { 15 result.push_back(current->val); 16 current = current->right; 17 } 18 else 19 { 20 /* Find the inorder predecessor of current */ 21 pre = current->left; 22 while(pre->right != NULL && pre->right != current) 23 pre = pre->right; 24 25 /* Make current as right child of its inorder predecessor */ 26 if(pre->right == NULL) 27 { 28 pre->right = current; 29 current = current->left; 30 } 31 32 /* Revert the changes made in if part to restore the original 33 tree i.e., fix the right child of predecssor */ 34 else 35 { 36 pre->right = NULL; 37 result.push_back(current->val); 38 current = current->right; 39 } /* End of if condition pre->right == NULL */ 40 } /* End of if condition current->left == NULL*/ 41 } /* End of while */ 42 43 return result; 44 }
那么,基于这个双指针遍历,可以把错置节点的判断逻辑加进去,就可以完美的在O(1)空间内,完成树的重构。
[Code]
改动代码如红字所示。增加了一个pointer -- parent来记录上一个访问节点。整个遍历过程中,使用(parent->val > current->val)来寻找违规节点,但是区别是,要获取第一次violation的parent和第二次violation的current,然后交换。
1 void recoverTree(TreeNode *root) 2 { 3 TreeNode *f1=NULL, *f2=NULL; 4 TreeNode *current,*pre, *parent=NULL; 5 6 if(root == NULL) 7 return; 8 bool found = false; 9 current = root; 10 while(current != NULL) 11 { 12 if(current->left == NULL) 13 { 14 if(parent && parent->val > current->val) 15 { 16 if(!found) 17 { 18 f1 = parent; 19 found = true; 20 } 21 f2 = current; 22 } 23 parent = current; 24 current = current->right; 25 } 26 else 27 { 28 /* Find the inorder predecessor of current */ 29 pre = current->left; 30 while(pre->right != NULL && pre->right != current) 31 pre = pre->right; 32 33 /* Make current as right child of its inorder predecessor */ 34 if(pre->right == NULL) 35 { 36 pre->right = current; 37 current = current->left; 38 } 39 40 /* Revert the changes made in if part to restore the original 41 tree i.e., fix the right child of predecssor */ 42 else 43 { 44 pre->right = NULL; 45 if(parent->val > current->val) 46 { 47 if(!found) 48 { 49 f1 = parent; 50 found = true; 51 } 52 f2 = current; 53 } 54 parent = current; 55 current = current->right; 56 } /* End of if condition pre->right == NULL */ 57 } /* End of if condition current->left == NULL*/ 58 } /* End of while */ 59 60 if(f1 && f2) 61 swap(f1->val, f2->val); 62 }