Tree, BST
递归时,要考虑状态可能的不连续性。
取左右子树的min / max 之后加上当前节点是最常见的递归结构,保证path自顶向下的连续。
BST做inorder traversal得到的结果是sorted。
BST中root的左子树所有点小于root;右子树所有点大于root。
一个正确的BST中每一个点都有其取值闭区间,为[左子树max, 右子树min],最左右两端的点有一端放开区间。
维护大小为k的MaxHeap
PriorityQueue<Integer> queue = new PriorityQueue<Integer>(k, Collections.reverseOrder());
Tree类问题中,递归转迭代的常用手法,就是利用尾递归。
Closest Binary Search Tree Value
所谓“最近的点”,可能是parent,可能是child,可能在左边,可能在右边。
- 所以一定要存好prev
- 两边都要探,不要沿着一边硬走。
递归的解法:确保不要去走null的点
public class Solution {
public int closestValue(TreeNode root, double target) {
return helper(root, target, root.val);
}
private int helper(TreeNode root, double target, int prev) {
if (root == null) {
return -1;
}
prev = Math.abs(prev - target) > Math.abs(root.val - target) ? root.val : prev;
if (root.left != null && root.val > target) {
return helper(root.left, target, prev);
} else if (root.right != null && root.val < target) {
return helper(root.right, target, prev);
}
return prev;
}
}
迭代的解法:这样写更简洁, 而且不用考虑node为空的情况。
public class Solution {
public int closestValue(TreeNode root, double target) {
int result = root.val;
while (root != null) {
if (Math.abs(result - target) > Math.abs(root.val - target)) {
result = root.val;
}
root = root.val > target ? root.left : root.right;
}
return result;
}
}
Validate Binary Search Tree
解法一:postorder traversal,把left child和right child的信息已经当前node的信息汇总,来判断是不是valid bst,然后把这个信息向上传递。
public class Solution {
public boolean isValidBST(TreeNode root) {
return helper(root).isBST;
}
private ReturnType helper(TreeNode root) {
if (root == null) {
return new ReturnType(true, Integer.MAX_VALUE, Integer.MIN_VALUE);
}
ReturnType left = helper(root.left);
ReturnType right = helper(root.right);
if (!left.isBST || !right.isBST) {
return new ReturnType(false, 0, 0);
}
if (root.left != null && left.max >= root.val || root.right != null && right.min <= root.val) {
return new ReturnType(false, 0, 0);
}
return new ReturnType(true, Math.min(left.min, root.val), Math.max(right.max, root.val));
}
}
class ReturnType {
boolean isBST;
int min, max;
public ReturnType(boolean isBST, int min, int max) {
this.isBST = isBST;
this.min = min;
this.max = max;
}
}
解法二:看到bst的题就想下inorder traversal的思路。
public class Solution {
public boolean isValidBST(TreeNode root) {
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode current = root, prev = null;
while (current != null || !stack.isEmpty()) {
while (current != null) {
stack.push(current);
current = current.left;
}
TreeNode node = stack.pop();
if (prev != null && prev.val >= node.val) {
return false;
}
prev = node;
current = node.right;
}
return true;
}
}
Kth Smallest Element in a BST
这题考察的就是BST的性质,in order = sorted
follow up1: what if you call this function multiple times?
- answer: 在解法一中,用hashmap存一下node和其对应的number of left nodes的信息。
follow up2: what if BST is modified (insert / delete) often?
- answer: 维护一个max heap。insert好办,如果是发生了delete operation,那就看delete的那个在不在这个max heap里,如果在的话,把这个node从max heap删除。
解法一:binary search solution
public class Solution {
public int kthSmallest(TreeNode root, int k) {
int leftNodes = countNodes(root.left);
if (leftNodes == k - 1) {
return root.val;
} else if (leftNodes > k - 1) {
return kthSmallest(root.left, k);
} else {
return kthSmallest(root.right, k - leftNodes - 1);
}
}
private int countNodes(TreeNode root) {
if (root == null) {
return 0;
}
int left = countNodes(root.left);
int right = countNodes(root.right);
return 1 + left + right;
}
}
解法二:inorder traversal recursive
public class Solution {
int count = 0;
int result = 0;
public int kthSmallest(TreeNode root, int k) {
inorder(root, k);
return result;
}
private void inorder(TreeNode root, int k) {
if (root == null) {
return;
}
inorder(root.left, k);
count++;
if (count == k) {
result = root.val;
}
inorder(root.right, k);
}
}
解法三: inorder traversal iterative
public class Solution {
public int kthSmallest(TreeNode root, int k) {
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode current = root;
int count = 0;
while (current != null || !stack.isEmpty()) {
while (current != null) {
stack.push(current);
current = current.left;
}
TreeNode node = stack.pop();
count++;
if (count == k) {
return node.val;
}
current = node.right;
}
return -1; // never hits
}
}
Inorder Successor in BST
iterative solution
public class Solution {
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
if (root == null || p == null) {
return null;
}
TreeNode successor = null;
while (root != null) {
if (root.val > p.val) {
successor = root;
root = root.left;
} else {
root = root.right;
}
}
return successor;
}
}
recursive solution
public class Solution {
TreeNode successor = null;
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
if (root == null || p == null) {
return successor;
}
if (root.val > p.val) {
successor = root;
return inorderSuccessor(root.left, p);
} else {
return inorderSuccessor(root.right, p);
}
}
}
Convert Sorted Array to Binary Search Tree
利用BST inorder = sorted的性质
在BST里多用区间的思想思考问题
解法一:recursive solution
public class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
if (nums == null || nums.length == 0) {
return null;
}
return helper(nums, 0, nums.length - 1);
}
private TreeNode helper(int[] nums, int start, int end) {
if (start > end) {
return null;
} else if (start == end) {
return new TreeNode(nums[start]);
}
int mid = (start + end) / 2;
TreeNode root = new TreeNode(nums[mid]);
root.left = helper(nums, start, mid - 1);
root.right = helper(nums, mid + 1, end);
return root;
}
}
解法二:iterative solution, 建一个Tuple类,存下start index, end index的信息
public class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
if (nums == null || nums.length == 0) {
return null;
}
int start = 0, end = nums.length - 1;
int mid = (start + end) / 2;
TreeNode root = new TreeNode(nums[mid]);
Queue<Tuple> queue = new LinkedList<Tuple>();
queue.offer(new Tuple(root, start, mid, end));
while (!queue.isEmpty()) {
Tuple t = queue.poll();
if (t.start <= t.mid - 1) {
int leftStart = t.start, leftEnd = t.mid - 1;
int leftMid = (leftStart + leftEnd) / 2;
TreeNode left = new TreeNode(nums[leftMid]);
t.node.left = left;
queue.offer(new Tuple(left, leftStart, leftMid, leftEnd));
}
if (t.mid + 1 <= t.end) {
int rightStart = t.mid + 1, rightEnd = t.end;
int rightMid = (rightStart + rightEnd) / 2;
TreeNode right = new TreeNode(nums[rightMid]);
t.node.right = right;
queue.offer(new Tuple(right, rightStart, rightMid, rightEnd));
}
}
return root;
}
}
class Tuple {
TreeNode node;
int start, mid, end;
public Tuple(TreeNode node, int start, int mid, int end) {
this.node = node;
this.start = start;
this.mid = mid;
this.end = end;
}
}
Verify Preorder Sequence in Binary Search Tree
只给定一个preorder顺序是无法生成唯一binary tree的,也可以生成多个valid BST。
所以这题的题意需要澄清下,正确意思是给定一个preorder sequence,只要这个sequence可以生成一个valid BST,都返回true。
这样我们的判断条件变成了这样,给定一个array如5(123)(678),第一个元素一定为root,后面的比root小的连续序列为左子树,后面比root大的连续序列为右子树。
左右子树可以为空,但是不能违反BST子树的性质。所以如果>root的连续数组后面又出现了<root的元素,一定为false。
divide conquer,时间复杂度为O(nlogn)
public class Solution {
public boolean verifyPreorder(int[] preorder) {
if (preorder == null || preorder.length <= 2) {
return true;
}
return helper(preorder, 0, preorder.length - 1);
}
private boolean helper(int[] preorder, int start, int end) {
if (end - start <= 1) {
return true;
}
int root = preorder[start];
int rightChildStart = start + 1;
for (int i = start + 1; i <= end; i++) {
if (preorder[i] < root) {
rightChildStart++;
} else {
break;
}
}
for (int i = rightChildStart; i <= end; i++) {
if (root > preorder[i]) {
return false;
}
}
return helper(preorder, start + 1, rightChildStart - 1) && helper(preorder, rightChildStart, end);
}
}
Recover Binary Search Tree
recursive solution O(1) space
public class Solution {
TreeNode first = null, second = null;
TreeNode prev = null;
public void recoverTree(TreeNode root) {
inorder(root);
if (first != null && second != null) {
int temp = first.val;
first.val = second.val;
second.val = temp;
}
}
private void inorder(TreeNode root) {
if (root == null) {
return;
}
inorder(root.left);
if (prev == null) {
prev = root;
} else {
if (prev.val > root.val) {
if (first == null) {
first = prev;
second = root;
} else {
second = root;
}
}
prev = root;
}
inorder(root.right);
}
}
iterative solution: O(h) space
public class Solution {
public void recoverTree(TreeNode root) {
TreeNode first = null, second = null, prev = null;
TreeNode current = root;
Stack<TreeNode> stack = new Stack<TreeNode>();
while (current != null || !stack.isEmpty()) {
while (current != null) {
stack.push(current);
current = current.left;
}
TreeNode node = stack.pop();
if (prev == null) {
prev = node;
} else {
if (prev.val > node.val) {
if (first == null) {
first = prev;
second = node;
} else {
second = node;
}
}
prev = node;
}
current = node.right;
}
if (first != null && second != null) {
int temp = first.val;
first.val = second.val;
second.val = temp;
}
}
}