搜索此博客

2012年8月31日星期五

Validate Binary Search Tree

Given a binary tree, determine if it is a valid binary search tree.

Solution: it is often easy to make mistakes in this problem. You should not only compare the value of a node with its left and right. The key point is to make sure that ALL Nodes in the left subtree must be smaller than the current node, and ALL Nodes in the left subtree should be greater than the current node. Therefore, we use a (min, max) interval to check the valid range of nodes in the left subtree and right subtree respectively.

Java语言: ValidateBST
01 public boolean isValidBST(TreeNode root) {
02     return isValidBSTHelper(root, Integer.MIN_VALUE, Integer.MAX_VALUE);
03 }
04 
05 public boolean isValidBSTHelper(TreeNode root, int min, int max)
06 {
07     if(root==null)  return true;
08     if(root.val<=min || root.val>=max)
09         return false;
10     return isValidBSTHelper(root.left, min, root.val) && isValidBSTHelper(root.right, root.val, max);
11 }

2012年8月30日星期四

Unique Binary Search Trees


Given n, how many structurally unique BST's (binary search trees) that store values 1...n?
For example,
Given n = 3, there are a total of 5 unique BST's.
   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3

Let's first see some basic cases,
If n==0, the root is null, then the number of BST is 1. numTrees(0)=1.
If n==1, the root is 1, then the number of BST is 1. numTrees(1)=1.
If n==2, we can either has 1(root)2(right) or 2(root)1(left), then the number of BST is 2. numTrees(2)=2.
The key idea of this problem is that each tree of n can be constructed by setting its root from 1, 2.. to n, and construct it's left tree and right tree recursively. For example, in the above diagram, we iterate from 1 to 3, and for 1, we can only have right subtrees with two nodes, then we calll numTrees(0) *numTrees(2) and add the result. then we pick 2 to be the root, and we can only have 2 and 3 as left and right trees accordingly, which is to call numTrees(1) * numTrees (1). Then we pick 3 as root, we call numTrees(2)*numTrees(0). However, we even do not need a recursion, but use an array to keep intermediate result. Further, you can find that the result is quite symmetric, that we even do not need to iterate a full cycle, but just reach the half, i.e., n/2. The second half should have the same number of trees as the first half.

01 public int numTrees(int n) {
02     if(n==0)
03         return 1;
04     if(n==1||n==2)
05         return n;
06     int[] array=new int[n+1];
07   
08     array[0]=1;
09     array[1]=1;
10     array[2]=2;
11   
12     for(int i=3;i<=n;i++)
13     {
14         int j;
15         for(j=1;j<=i/2;j++)
16         {
17             array[i]+=array[i-j]*array[j-1]*2;
18         }
19       
20         if(i%2==1)
21         {
22             array[i]+=array[i-j]*array[i-j];
23         }          
24     }      
25     return array[n];      
26 }

Search Rotated Sorted Array


Suppose a sorted array is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).
You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.
The key idea is to know that there must be one half side is in normal order. Remember this, then the problem is easy to solve.

01 public int search(int[] A, int target) {
02     return binarySearch(A,0,A.length-1,target);
03 }
04 
05 public int binarySearch(int []A, int start, int end, int target)
06 {
07     if(start>end)
08         return -1;
09     int mid=start+(end-start)/2;
10     if(A[mid]==target)
11     {
12         return mid;
13     }
14     else
15     {
16         if(A[start]<A[mid]) //first half is in order
17         {
18             if(target>=A[start]&&target<=A[mid]) //target in first half, just search that half
19             {
20                 return binarySearch(A,start,mid-1,target);
21             }
22             else //target in the second half, search it recursively
23             {
24                 return binarySearch(A,mid+1,end,target);
25             }
26         }
27         else if(A[start]>A[mid]) //first half is rotated, then second half is in order
28         {
29             if(target>=A[mid] && target<=A[end]) //target is in the second half
30             {
31                 return binarySearch(A,mid+1,end,target);
32             }
33             else //target is in the first half, search for it recursively
34             {
35                 return binarySearch(A,start,mid-1,target);
36             }
37         }
38         else //A[start]==A[mid], search the second half
39         {
40             return binarySearch(A,mid+1,end,target);
41         }
42         
43     }
44 }


Follow up for "Search in Rotated Sorted Array":

What if duplicates are allowed?

Would this affect the run-time complexity? How and why?

Write a function to determine if a given target is in the array.

If duplicates are allowed, the question becomes more tricky. We need to handle the duplication cases specially. See line 37 -- line 50.


01 public boolean search(int[] A, int target) {
02     return binarySearch(A,0,A.length-1,target);
03 }
04 
05 public boolean binarySearch(int []A, int start, int end, int target)
06 {
07     if(start>end)
08         return false;
09     int mid=start+(end-start)/2;
10     if(A[mid]==target)
11     {
12         return true;
13     }
14   
15     if(A[start]<A[mid])
16     {
17         if(target>=A[start]&&target<=A[mid])
18         {
19             return binarySearch(A,start,mid-1,target);
20         }
21         else
22         {
23             return binarySearch(A,mid+1,end,target);
24         }
25     }
26     else if(A[start]>A[mid])
27     {
28         if(target>=A[mid] && target<=A[end])
29         {
30             return binarySearch(A,mid+1,end,target);
31         }
32         else
33         {
34             return binarySearch(A,start,mid-1,target);
35         }
36     }
37     else if(A[start]==A[mid])
38     {
39         if(A[mid]!=A[end])
40         {
41             return binarySearch(A,mid+1,end,target);
42         }
43         else
44         {                                  
45             boolean result=binarySearch(A,start,mid-1,target);
46             if(result==false)
47                 return binarySearch(A,mid+1,end,target);
48             else
49                 return result;                  
50         }
51       
52     }
53     return false;  
54 }

Search for a Range

Given a sorted array of integers, find the starting and ending position of a given target value.
Your algorithm's runtime complexity must be in the order of O(log n).
If the target is not found in the array, return [-1, -1].
For example,
Given [5, 7, 7, 8, 8, 10] and target value 8,
return [3, 4].
This question looks very much like the binary search problem. But we may have multiple target values, and we need to find the first position and the last position of that. What should we do? An insight just come to mind is that we should find the last number that is smaller than the target, as well as the first number that is greater than the target. They are found by the functions of bsearchlower() and bsearchupper() respectively. We call this pair of indexes "left" and "right". When we find them, we should check whether A[++left] equals the target value. If it is, we then check whether A[--right] equals the target value. and than the left, right will be the index we want to return. Otherwise the target does not exist in the array and we just return [-1,-1].

01 public int[] searchRange(int[] A, int target) {
02     int[]result=new int[2];
03     int lo=0;
04     int hi=A.length-1;
05     if(A[hi]<target ||A[lo]>target )
06     {
07         result[0]=-1;
08         result[1]=-1;
09         return result;
10     }
11     int left=bsearchlower(A,target,lo,hi);
12     if(A[left]<target)
13         left=left+1;      
14     if(A[left]==target)
15     {
16         result[0]=left;
17         int right=bsearchupper(A,target,lo,hi);
18         if(A[right]>target)      
19             right=right-1;      
20         if(A[right]==target)
21             result[1]=right;        
22         else
23         {
24             result[0]=-1;
25             result[1]=-1;                          
26         }          
27     }
28   
29     else
30     {
31         result[0]=-1;
32         result[1]=-1;                      
33     }      
34     return result;      
35 }
36 
37 //Find the last number that is smaller than target
38 public int bsearchlower(int [] A, int target, int lo, int hi)
39 {
40     if(lo>hi||target<A[lo]||target>A[hi])
41     {
42         return -1;
43     }
44     int mid=(lo+hi+1)/2;
45     while(lo<hi)
46     {
47         if(A[mid]<target)
48         {
49             lo=mid;
50         }
51         else
52         {
53             hi=mid-1;
54         }
55         mid=(lo+hi+1)/2;
56     }
57     return mid;
58 }
59 
60 //Find the first number that is greater than target
61 public int bsearchupper(int[] A, int target, int lo, int hi)
62 {
63     if(lo>hi ||target<A[lo]||target>A[hi])
64     {
65         return -1;
66     }
67     int mid=(lo+hi)/2;
68     while(lo<hi)
69     {
70         if(A[mid]>target)
71         {
72             hi=mid;
73         }
74         else
75         {
76             lo=mid+1;
77         }
78         mid=(lo+hi)/2;
79     }
80     return mid;
81 }

2012年8月28日星期二

Binary Tree In-order Traversal

Today LeetCode has new problems about binary search trees. Thanks leetcode!

Given a binary tree, return the inorder traversal of its nodes' values.
For example:
Given binary tree {1,#,2,3},
   1
    \
     2
    /
   3
return [1,3,2].
Note: Recursive solution is trivial, could you do it iteratively?
We first look the recursive solution.
The tree node is defined as,
Java: TreeNode
public class TreeNode {
   int val;
   TreeNode left;
   TreeNode right;
   TreeNode(int x) { val = x; }
}

Then we write the in-order traversal method

01 public ArrayList<Integer> inorderTraversal(TreeNode root) {
02     ArrayList<Integer> result=new ArrayList<Integer>();
03     inorder(result,root);
04     return result;
05 }
06 
07 public void inorder(ArrayList<Integer> result,TreeNode root)
08 {
09     if(root==null)
10         return;
11     
12     if(root.left!=null)
13     {
14         inorder(result,root.left);
15     }
16     
17     result.add(root.val);
18     
19     if(root.right!=null)
20     {
21         inorder(result,root.right);
22     }
23 }

However, we can also solve it  iteratively. This can be done with the help of a stack. We add from root each node on the left to the stack, until we do not find a left node in the end. If the stack is not empty, we pop a node from it and print it. If the popped node has right child, we add it to the stack and track it to the left most node, until we cannot find a left node any more.


Java: BSTInOrder
01 public ArrayList<Integer> inorderTraversal(TreeNode root) {
02     ArrayList<Integer> result=new ArrayList<Integer>();
03     Stack<TreeNode> st=new Stack<TreeNode>();
04     if(root!=null)
05     {
06         st.push(root);
07         while(root.left!=null)
08         {
09             root=root.left;
10             st.push(root);
11         }
12     }
13     while(!st.empty())
14     {
15         TreeNode top=st.pop();
16         result.add(top.val);
17         if(top.right!=null)
18         {
19             st.push(top.right);
20             top=top.right;
21             while(top.left!=null)
22             {
23                 top=top.left;
24                 st.push(top);
25             }
26         }
27     }
28   
29     return result;
30 }

I believe there are solutions even without using a Stack. Please feel free to show your ideas.