Showing posts with label Depth-first Search. Show all posts
Showing posts with label Depth-first Search. Show all posts

Wednesday, January 21, 2015

Word Ladder II (LeetCode Backtracking)

Question: Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that:
Only one letter can be changed at a time
Each intermediate word must exist in the dictionary
For example,
Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]
Return
  [
    ["hit","hot","dot","dog","cog"],
    ["hit","hot","lot","log","cog"]
  ]

Idea: Dijkstra's algorithm. Image the words as nodes and the ladder as edges, this is exactly a shortest path problem from the node start (source) to the node end (destination). Without the Boost library in C++, we need to a little bit patience to implement the Dijkstra's algorithm in Java.

First add the start and the end to the dictionary. Then use breadth-first search to flood from the start node to all the reachable nodes (even further than end nodes), and label the distance between the current node and the start node. At the same time, for each node, create single direction edges to its source.

After the flood, start from the end node to construct path to the start node, since the edges we constructed are single directional and are from destination to source. Assume the distance between the node and the start node is x, then the next hop y of this node should follow two conditions:
1) y is x's neighbor (has constructed edge)
2) distance[y=>start]==distance[x=>start]-1
So following these two rules, use depth-first search to construct the path until the start node is reached. Do not forget to reverse the paths constructed before output, since the requirement is source to destination.


Time: O(n^2) (The fastest implementation of Dijkstra's algorithm is O(e+vlgv))
Space: O(n^2)

Code:
 public class Solution {  
   public List<List<String>> findLadders(String start, String end, Set<String> dict) {  
     List<List<String>> result=new ArrayList<List<String>>();  
     if(dict==null||dict.size()==0)  
       return result;  
     HashMap<String,List<String>> graph=new HashMap<String,List<String>>();  
     HashMap<String, Integer> distance=new HashMap<String,Integer>();  
     dict.add(start);  
     dict.add(end);  
     bfs(graph,distance,start,end,dict);  
     List<String> path=new ArrayList<String>();  
     dfs(result,path,graph,distance,start,end);  
     return result;  
   }  
   public void dfs(List<List<String>> result,List<String> path, HashMap<String,List<String>> graph,HashMap<String,Integer> distance,String start,String cur)  
   {  
     path.add(cur);  
     if(path.contains(start))  
     {  
       Collections.reverse(path);  
       result.add(new ArrayList<String>(path));  
       Collections.reverse(path);  
     }  
     else  
     {  
       for(String next:graph.get(cur))  
       {  
         if(distance.containsKey(next)&&distance.get(cur)==distance.get(next)+1)  
         {  
           dfs(result,path,graph,distance,start,next);  
         }  
       }  
     }  
     path.remove(path.size()-1);  
   }  
   public void bfs(HashMap<String,List<String>> graph, HashMap<String,Integer> distance,String start, String end, Set<String> dict)  
   {  
     Queue<String> queue=new LinkedList<String>();  
     queue.offer(start);  
     distance.put(start,0);  
     for(String s:dict)  
     {  
       graph.put(s,new ArrayList<String>());  
     }  
     while(queue.isEmpty()==false)  
     {  
       String cur=queue.poll();  
       List<String> neighbors=getNeighbors(cur,dict);  
       for(String neighbor:neighbors)  
       {  
         graph.get(neighbor).add(cur);  
         if(distance.containsKey(neighbor)==false)  
         {  
           distance.put(neighbor,distance.get(cur)+1);  
           queue.offer(neighbor);  
         }  
       }  
     }  
   }  
   public List<String> getNeighbors(String cur, Set<String> dict)  
   {  
     List<String> result=new ArrayList<String>();  
     for(int i=0;i<cur.length();i++)  
     {  
       for(char c='a';c<='z';c++)  
       {  
         if(c!=cur.charAt(i))  
         {  
           String maybe=replaceCharAt(cur,i,c);  
           if(dict.contains(maybe))  
             result.add(maybe);  
         }  
       }  
     }  
     return result;  
   }  
   public String replaceCharAt(String s, int index, char c)  
   {  
     char[] chars=s.toCharArray();  
     chars[index]=c;  
     return new String(chars);  
   }  
 }  

Tuesday, January 20, 2015

Permutations II (LeetCode Backtracking)

Question: Given a collection of numbers that might contain duplicates, return all possible unique permutations.
For example,
[1,1,2] have the following unique permutations:
[1,1,2], [1,2,1], and [2,1,1].

Idea: Backtracking depth-first search. To avoid duplication, we first sort the array. Then the integers with the same value are placed at adjacent positions. For each recursive call, we only add the integer at the first unused position and is the first of the unused of the adjacent subarray with the same value.

Time: O(n!) Space: O(n)

Code:
 public class Solution {  
   public List<List<Integer>> permuteUnique(int[] num) {  
     List<List<Integer>> result=new ArrayList<List<Integer>>();  
     List<Integer> path=new ArrayList<Integer>();  
     Arrays.sort(num);  
     boolean[] used=new boolean[num.length];  
     dfs(result,path,num,used);  
     return result;  
   }  
   public void dfs(List<List<Integer>> result, List<Integer> path,int[] num,boolean[] used)  
   {  
     if(path.size()==num.length)  
     {  
       result.add(new ArrayList<Integer>(path));  
       return;  
     }  
     for(int i=0;i<num.length;i++)  
     {  
       if((i!=0&&num[i]==num[i-1]&&used[i-1]==false)||used[i]==true)  
         continue;  
       path.add(num[i]);  
       used[i]=true;  
       dfs(result,path,num,used);  
       used[i]=false;  
       path.remove(path.size()-1);  
     }  
   }  
 }  

Permutations (LeetCode Backtracking)

Question: Given a collection of numbers, return all possible permutations.
For example,
[1,2,3] have the following permutations:
[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], and [3,2,1].

Idea: Backtracking depth-first search. For each unused integer x, put it at the head of the permutation, mark x as used, then recursively construct the permutation from the rest of unused integers. When we roll back, do not forget to re-mark x to unused, since that x can be used at other positions in the future.

Time: O(n!) Space: O(n)

Code:
 public class Solution {  
   public List<List<Integer>> permute(int[] num) {  
     List<List<Integer>> result=new ArrayList<List<Integer>>();  
     List<Integer> path=new ArrayList<Integer>();  
     boolean[] used=new boolean[num.length];  
     dfs(result,path,num);  
     return result;  
   }  
   public void dfs(List<List<Integer>> result,List<Integer> path,int[] num)  
   {  
     if(path.size()==num.length)  
     {  
       result.add(new ArrayList<Integer>(path));  
       return;  
     }  
     for(int i=0;i<num.length;i++)  
     {  
       if(path.contains(num[i])==false)  
       {  
         path.add(num[i]);  
         dfs(result,path,num);  
         path.remove(path.size()-1);  
       }  
     }  
   }  
 }  

N-Queens II (LeetCode Backtracking)

Question: Follow up for N-Queens problem.
Now, instead outputting board configurations, return the total number of distinct solutions.


Idea: Backtracking depth-first search. Since we can only place one queen in each row, we place the queens row by row. For each row, we try to place the queen in each column, if the placement is safe, then place the queen and go to the next row. When we reach row==n (out of boundary), that means the rows from 0=>n-1 is valid, add 1 to the result

Time: O(n^n) Space: O(n^2)

Code:
 public class Solution {  
   int counter;  
   public int totalNQueens(int n) {  
     if(n==0)  
       return 0;  
     counter=0;  
     char[][] board=new char[n][n];  
     for(int i=0;i<n;i++)  
       Arrays.fill(board[i],'.');  
     dfs(board,0);  
     return counter;  
   }  
   public void dfs(char[][] board,int curRow)  
   {  
     int n=board.length;  
     if(curRow==n)  
     {  
       counter+=1;  
       return;  
     }  
     for(int j=0;j<n;j++)  
     {  
       board[curRow][j]='Q';  
       if(isValid(board,curRow,j))  
       {  
         dfs(board,curRow+1);  
       }  
       board[curRow][j]='.';  
     }  
   }  
   public boolean isValid(char[][] board,int x,int y)  
   {  
     int n=board.length;  
     for(int i=0;i<n;i++)  
     {  
       for(int j=0;j<n;j++)  
       {  
         if(i!=x||j!=y)  
         {  
           if(i==x&&board[i][j]=='Q')  
             return false;  
           if(j==y&&board[i][j]=='Q')  
             return false;  
           if((i+j==x+y||i-j==x-y)&&(board[i][j]=='Q'))  
             return false;  
         }  
       }  
     }  
     return true;  
   }  
 }  

N-Queens (LeetCode Backtracking)

Question: The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.
Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space respectively.
For example,
There exist two distinct solutions to the 4-queens puzzle:
[
 [".Q..",  // Solution 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // Solution 2
  "Q...",
  "...Q",
  ".Q.."]
]

Idea: Brute force depth-first-search. First initial a char[][] filled with '.'s. For each row, we try to put a queen (set char[x][y]='Q') at any column. If it is valid (no conflict), place the queen then go to the next row. If we reach row==n (out of boundary), that means all the rows from 0=>n-1 are valid, append this assignment to the result.
For easier understanding, I wrote the check valid function in all the 8 directions separately: same row, same column, 2 diagonal, 2 counter- diagonal. The code presentation can be simplified by getting the distance to the boundaries first, but it will be a little bit harder to be understood. So I keep the a little bit longer but easier understood writing.

Time: O(n^n) Space: O(1)

Code:
 public class Solution {  
   public List<String[]> solveNQueens(int n) {  
     List<String[]> result=new ArrayList<String[]>();  
     if(n==0)  
       return result;  
     char[][] board=new char[n][n];  
     for(int i=0;i<n;i++)  
       Arrays.fill(board[i],'.');  
     dfs(result,board,0);  
     return result;  
   }  
   public void dfs(List<String[]> result,char[][] board,int curRow)  
   {  
     int n=board.length;  
     if(curRow==n)  
     {  
       result.add(toStringArray(board));  
       return;  
     }  
     for(int j=0;j<n;j++)  
     {  
       board[curRow][j]='Q';  
       if(isValid(board,curRow,j))  
       {  
         dfs(result,board,curRow+1);  
       }  
       board[curRow][j]='.';  
     }  
   }  
   public String[] toStringArray(char[][] board)  
   {  
     String[] result=new String[board.length];  
     for(int i=0;i<board.length;i++)  
     {  
       StringBuilder builder=new StringBuilder();  
       for(int j=0;j<board.length;j++)  
         builder.append(board[i][j]);  
       result[i]=builder.toString();  
     }  
     return result;  
   }  
   public boolean isValid(char[][] board, int x, int y)  
   {  
     int n=board.length;  
     for(int i=0;i<n;i++)  
     {  
       if(i!=x&&board[i][y]=='Q')  
         return false;  
     }  
     for(int j=0;j<n;j++)  
     {  
       if(j!=y&&board[x][j]=='Q')  
         return false;  
     }  
     for(int i=1;x+i<n&&y+i<n;i++)  
     {  
       if(board[x+i][y+i]=='Q')  
         return false;  
     }  
     for(int i=1;x-i>=0&&y-i>=0;i++)  
     {  
       if(board[x-i][y-i]=='Q')  
         return false;  
     }  
     for(int i=1;x-i>=0&&y+i<n;i++)  
     {  
       if(board[x-i][y+i]=='Q')  
         return false;  
     }  
     for(int i=1;x+i<n&&y-i>=0;i++)  
     {  
       if(board[x+i][y-i]=='Q')  
         return false;  
     }  
     return true;  
   }  
 }  

Combinations (LeetCode Backtracking)

Question: Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.
For example,
If n = 4 and k = 2, a solution is:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

Idea: Depth-first search. To avoid duplication, start from 1, then only add numbers bigger than all the current numbers to the set until the set size is k. Then collect all the combinations.

Time: O(n!) Space: O(n)

Code:
 public class Solution {  
   public List<List<Integer>> combine(int n, int k) {  
     List<List<Integer>> result=new ArrayList<List<Integer>>();  
     List<Integer> path=new ArrayList<Integer>();  
     dfs(result,path,1,n,k);  
     return result;  
   }  
   public void dfs(List<List<Integer>> result, List<Integer> path, int start, int end, int k)  
   {  
     if(k==path.size())  
     {  
       result.add(new ArrayList<Integer>(path));  
       return;  
     }  
     for(int i=start;i<=end;i++)  
     {  
       path.add(i);  
       dfs(result,path,i+1,end,k);  
       path.remove(path.size()-1);  
     }  
   }  
 }  

Monday, January 19, 2015

Word Break II (LeetCode Dynamic Programming)

Question: Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each word is a valid dictionary word.
Return all such possible sentences.
For example, given
s = "catsanddog",
dict = ["cat", "cats", "and", "sand", "dog"].
A solution is ["cats and dog", "cat sand dog"].

Idea: Depth-first search with recursive dynamic programming. At each position of the input string s, break it to two halves, if the prefix is contained in the dict, recursively break the suffix to words. Use a hashmap to cache the broken strings computed before.

Time: O(n^2) Space: O(n^2)

Code:
 public class Solution {  
   public List<String> wordBreak(String s, Set<String> dict) {  
     HashMap<String,List<String>> map=new HashMap<String,List<String>>();  
     return dfs(s,dict,map);  
   }  
   public List<String> dfs(String s, Set<String> dict, HashMap<String,List<String>> map)  
   {  
     if(map.containsKey(s))  
       return map.get(s);  
     List<String> result=new ArrayList<String>();  
     int n=s.length();  
     if(n<=0)  
     {  
       map.put(s,result);  
       return result;  
     }  
     for(int i=1;i<=s.length();i++)  
     {  
       String prefix=s.substring(0,i);  
       if(dict.contains(prefix))  
       {  
         if(prefix.length()==s.length())  
           result.add(prefix);  
         else  
         {  
           String suffix=s.substring(i);  
           List<String> breakSuffix=dfs(suffix,dict,map);  
           for(String tmp:breakSuffix)  
             result.add(prefix+" "+tmp);  
         }  
       }  
     }  
     map.put(s,result);  
     return result;  
   }  
 }  

Palindrome Partitioning (LeetCode Backtracking)

Question: Given a string s, partition s such that every substring of the partition is a palindrome.
Return all possible palindrome partitioning of s.
For example, given s = "aab",
Return

  [
    ["aa","b"],
    ["a","a","b"]
  ]

Idea: Brute force depth-first search. For the input string, split it to two halves at any "valid" positions, where valid means the prefix is a valid palindrome. Then recursively partition the suffix part.

Time: O(n^n) Space: O(n)

Code:
 public class Solution {  
   public List<List<String>> partition(String s) {  
     List<List<String>> result=new ArrayList<List<String>>();  
     List<String> path=new ArrayList<String>();  
     dfs(result,path,0,s);  
     return result;  
   }  
   public boolean isValid(String s)  
   {  
     for(int i=0;i<s.length()/2;i++)  
     {  
       if(s.charAt(i)!=s.charAt(s.length()-1-i))  
         return false;  
     }  
     return true;  
   }  
   public void dfs(List<List<String>> result, List<String> path, int cur,String s)  
   {  
     if(cur==s.length())  
     {  
       result.add(new ArrayList<String>(path));  
       return;  
     }  
     for(int i=cur+1;i<=s.length();i++)  
     {  
       String prefix=s.substring(cur,i);  
       if(isValid(prefix)==false)  
         continue;  
       String next=s.substring(i,s.length());  
       path.add(prefix);  
       dfs(result,path,i,s);  
       path.remove(path.size()-1);  
     }  
   }  
 }  

Friday, January 16, 2015

Validate Binary Search Tree (LeetCode Tree)

Question: Given a binary tree, determine if it is a valid binary search tree (BST).
Assume a BST is defined as follows:
The left subtree of a node contains only nodes with keys less than the node's key.
The right subtree of a node contains only nodes with keys greater than the node's key.
Both the left and right subtrees must also be binary search trees.

Idea: In order traversal. If the previous node is larger or equal to the current node, return false and terminate the traversal.

Time: O(n) Space: O(lgn)

Code:
 public class Solution {  
   int prev;  
   boolean firstNode;  
   public boolean isValidBST(TreeNode root) {  
     firstNode=true;  
     return inorder(root);  
   }  
   public boolean inorder(TreeNode cur)  
   {  
     if(cur==null)  
       return true;  
     if(inorder(cur.left)==false)  
       return false;  
     if(!firstNode&&prev>=cur.val)  
       return false;  
     prev=cur.val;  
     firstNode=false;  
     if(inorder(cur.right)==false)  
       return false;  
     return true;  
   }  
 }  

Thursday, January 15, 2015

Symmetric Tree (LeetCode Tree)

Question: Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).
For example, this binary tree is symmetric:
    1
   / \
  2   2
 / \ / \
3  4 4  3
But the following is not:
    1
   / \
  2   2
   \   \
   3    3

Idea: Depth-first search. I used preorder traversal, compare current, then traverse (left.left, right.right) and (left.right, right.left) at the same time. Once a mismatch happens, stops and return.

Time: O(n) Space: O(lgn)

Code:
 public class Solution {  
   public boolean isSymmetric(TreeNode root) {  
     if(root==null)  
       return true;  
     return dfs(root.left,root.right);  
   }  
   public boolean dfs(TreeNode left, TreeNode right)  
   {  
     if(left==null||right==null)  
     {  
       if(left==null&&right==null)  
         return true;  
       return false;  
     }  
     if(left.val!=right.val)  
       return false;  
     return dfs(left.left,right.right)&&dfs(left.right,right.left);  
   }  
 }  

Sum Root to Leaf Numbers (LeetCode Tree)

Question: Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number.
An example is the root-to-leaf path 1->2->3 which represents the number 123.
Find the total sum of all root-to-leaf numbers.
For example,
    1
   / \
  2   3
The root-to-leaf path 1->2 represents the number 12.
The root-to-leaf path 1->3 represents the number 13.
Return the sum = 12 + 13 = 25.

Idea: Depth-first search. Use a class member variable to record the result. Calculate the partial results along the search. Once a leaf is visited, update the total result.

Time: O(n) Space: O(lgn)

Code: 
 public class Solution {  
   int result;  
   public int sumNumbers(TreeNode root) {  
     result=0;  
     int lastSum=0;  
     dfs(lastSum,root);  
     return result;  
   }  
   public void dfs(int lastSum,TreeNode cur)  
   {  
     if(cur==null)  
       return;  
     if(cur.left==null&&cur.right==null)  
     {  
       result+=lastSum*10+cur.val;  
       return;  
     }  
     dfs(lastSum*10+cur.val,cur.left);  
     dfs(lastSum*10+cur.val,cur.right);  
   }  
 }  

Same Tree (LeetCode Tree)

Question: Given two binary trees, write a function to check if they are equal or not.
Two binary trees are considered equal if they are structurally identical and the nodes have the same value.

Idea: Any order traversal works. I used preorder traversal and use a class member variable to stop the traversal if the trees have been identified as different.

Time: O(n) Space: O(lgn)

Code:
 public class Solution {  
   boolean notSame;  
   public boolean isSameTree(TreeNode p, TreeNode q) {  
     notSame=false;  
     traverse(p,q);  
     return !notSame;  
   }  
   public void traverse(TreeNode p, TreeNode q)  
   {  
     if(notSame==true)  
       return;  
     if(p==null||q==null)  
     {  
       if(p!=null||q!=null)  
         notSame=true;  
       return;  
     }  
     if(p.val!=q.val)  
     {  
       notSame=true;  
       return;  
     }  
     traverse(p.left,q.left);  
     traverse(p.right,q.right);  
   }  
 }  

Recover Binary Search Tree (LeetCode Tree)

Question: Two elements of a binary search tree (BST) are swapped by mistake.
Recover the tree without changing its structure.

Idea: In order traversal, use two class member variables to record the two abnormal nodes. Then swap their values.

Time: O(n) Space: O(1)

Code:
 public class Solution {  
   TreeNode v1,v2,prev;  
   public void recoverTree(TreeNode root) {  
     v1=null;  
     v2=null;  
     prev=new TreeNode(Integer.MIN_VALUE);  
     traversal(root);  
     int tmp=v1.val;  
     v1.val=v2.val;  
     v2.val=tmp;  
   }  
   public void traversal(TreeNode root)  
   {  
     if(root==null)  
       return;  
     traversal(root.left);  
     if(v1==null&&prev.val>=root.val)  
       v1=prev;  
     if(v1!=null&&prev.val>=root.val)  
       v2=root;  
     prev=root;  
     traversal(root.right);  
   }  
 }  

Populating Next Right Pointers in Each Node II (LeetCode Tree)

Question: Follow up for problem "Populating Next Right Pointers in Each Node".
What if the given tree could be any binary tree? Would your previous solution still work?
Note:
You may only use constant extra space.
For example,
Given the following binary tree,
         1
       /  \
      2    3
     / \    \
    4   5    7
After calling your function, the tree should look like:
         1 -> NULL
       /  \
      2 -> 3 -> NULL
     / \    \
    4-> 5 -> 7 -> NULL

Idea: Both depth-first search and breadth-first search work with the same time complexity. However, depth-first search is more space efficient. For depth-first search, always keeps track of the previous node to jump through the missing leaves. For breadth-first search, use a queue to remember each row.

Time: O(n) Space: O(1)

Code:
 public class Solution {  
   public void connect(TreeLinkNode root) {  
     if(root==null)  
       return;  
     TreeLinkNode parent=root;  
     TreeLinkNode pre;  
     TreeLinkNode next;  
     while(parent!=null)  
     {  
       pre=null;  
       next=null;  
       while(parent!=null)  
       {  
         if(next==null)  
           next=(parent.left!=null)?parent.left:parent.right;  
         if(parent.left!=null)  
         {  
           if(pre!=null)  
           {  
             pre.next=parent.left;  
             pre=pre.next;  
           }  
           else  
             pre=parent.left;  
         }  
         if(parent.right!=null)  
         {  
           if(pre!=null)  
           {  
             pre.next=parent.right;  
             pre=pre.next;  
           }  
           else  
             pre=parent.right;  
         }  
         parent=parent.next;  
       }  
       parent=next;  
     }  
   }  
 }  

Path Sum II (LeetCode Tree)

Question: Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum.
For example:
Given the below binary tree and sum = 22,
              5
             / \
            4   8
           /   / \
          11  13  4
         /  \    / \
        7    2  5   1
return
[
   [5,4,11,2],
   [5,8,4,5]
]

Idea: Classic depth-first search problem. Keep going down the tree until reach a leaf, if the leaf's value equals the target, add the value to the path and add the path to the result. When the search goes up, do not forget to pop the current value from the path since the next search is in another route.

Time: O(n) Space: O(lgn)

Code:
 public class Solution {  
   public List<List<Integer>> pathSum(TreeNode root, int sum) {  
     List<List<Integer>> result=new ArrayList<List<Integer>>();  
     List<Integer> path=new ArrayList<Integer>();  
     dfs(result,path,root,sum);  
     return result;  
   }  
   public void dfs(List<List<Integer>> result,List<Integer> path, TreeNode cur, int target)  
   {  
     if(cur==null)  
       return;  
     if(cur.left==null&&cur.right==null)  
     {  
       if(cur.val==target)  
       {  
         path.add(cur.val);  
         result.add(new ArrayList<Integer>(path));  
         path.remove(path.size()-1);  
       }  
       return;  
     }  
     path.add(cur.val);  
     dfs(result,path,cur.left,target-cur.val);  
     dfs(result,path,cur.right,target-cur.val);  
     path.remove(path.size()-1);  
   }  
 }  

Sunday, January 11, 2015

Regular Expression Matching (LeetCode String)

Question: Implement regular expression matching with support for '.' and '*'.
'.' Matches any single character.
'*' Matches zero or more of the preceding element.
The matching should cover the entire input string (not partial).
The function prototype should be:
bool isMatch(const char *s, const char *p)
Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "a*") → true
isMatch("aa", ".*") → true
isMatch("ab", ".*") → true
isMatch("aab", "c*a*b") → true

Idea: Brute force depth-first search.
1) If p is empty, return false;
2) compare the first character, if there is a match, continue to match the rest of characters recursively, otherwise return false;

Time: O(n^2) Space: O(n)

Code: 
 public class Solution {  
   public boolean isMatch(String s, String p) {  
     if(p.length()==0)  
       return s.length()==0;  
     if(p.length()==1||p.charAt(1)!='*')  
     {  
       if(s.length()==0||(p.charAt(0)!='.'&&s.charAt(0)!=p.charAt(0)))  
       {  
         return false;  
       }  
       return isMatch(s.substring(1),p.substring(1));  
     }  
     else  
     {  
       int len=s.length();  
       int i=-1;  
       while(i<len&&(i<0||p.charAt(0)=='.'||p.charAt(0)==s.charAt(i)))  
       {  
         if(isMatch(s.substring(i+1),p.substring(2)))  
           return true;  
         i++;  
       }  
       return false;  
     }  
   }  
 }  

Saturday, January 10, 2015

Letter Combinations of a Phone Number (LeetCode String)

Question: Given a digit string, return all possible letter combinations that the number could represent.
A mapping of digit to letters (just like on the telephone buttons) is given below.
Input:Digit string "23"
Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

Idea: Depth-first search. First initial the digit - char[] mapping, e.g. 2->{a, b, c}. Then add the char in char[] one by one and continue to append the char created by the next digit. Then backtrack to the original point to add another char. For example, "23"
2->{a,b,c}
3->{d,e,f}

a, ad, ae, af
b, bd, be, bf
c, cd, ce, cf.

When the length of a temporary string matches the required length digits.length(), add it to the result.

Time: O(n^4) since each digit has maximum 4 choices.
Space: O(n) (each time we just cache a temporary string)

Code:
 public class Solution {  
   public List<String> letterCombinations(String digits) {  
     List<String> result=new ArrayList<String>();  
     if(digits.length()==0)  
     {    
       result.add("");  
       return result;  
     }  
     StringBuilder path=new StringBuilder();  
     dfs(result,path,digits,0);  
     return result;  
   }  
   public void dfs(List<String> result,StringBuilder path,String digits,int cur)  
   {  
     if(path.length()==digits.length())  
     {  
       result.add(path.toString());  
       return;  
     }  
     int value=(int)(digits.charAt(cur)-'0');  
     for(char c:digitToString(value))  
     {  
       path.append(c);  
       dfs(result,path,digits,cur+1);  
       path.deleteCharAt(path.length()-1);  
     }  
   }  
   HashMap<Integer,char[]> computed=new HashMap<Integer,char[]>();  
   public char[] digitToString(int i)  
   {  
     if(computed.containsKey(i))  
       return computed.get(i);  
     String[] letters={  
       " ",  
       " ",  
       "abc",  
       "def",  
       "ghi",  
       "jkl",  
       "mno",  
       "qprs",  
       "tuv",  
       "wxyz"  
     };  
     char[] result=letters[i].toCharArray();  
     computed.put(i,result);  
     return result;  
   }  
 }  

Sunday, January 4, 2015

Word Search (LeetCode Array)

Question: Given a 2D board and a word, find if the word exists in the grid.
The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.
For example,
Given board =
[
  ["ABCE"],
  ["SFCS"],
  ["ADEE"]
]
word = "ABCCED", -> returns true,
word = "SEE", -> returns true,
word = "ABCB", -> returns false.

Idea: Depth-first search with backtracking. For each block, start to match the string one character by one character. Whenever a character board[i][j] is matched, mark it as board[i][j]='#' (representing it has been used) then go forward to board[i+1][j], board[i-1][j], board[i][j+1] and board[i][j-1]. If board[i][j] is out of boundary or board[next] does not match with string[next], return false. When all the characters of the string is matched, return true. When we roll back to the point board[i][j], do not forget to set it back to the original character, otherwise when we initial another search from the next point, e.g. board[i][j-1], the path can not go left, since it is '#'.

Time: O(n^4) Space: O(n^2)

Code:
 public class Solution {  
   public boolean exist(char[][] board, String word) {  
     if(board.length==0||board[0].length==0||board.length*board[0].length<word.length())  
       return false;  
     boolean result=false;  
     for(int i=0;i<board.length;i++)  
     {  
       for(int j=0;j<board[0].length;j++)  
       {  
         result=result||dfs(board,word,i,j,0);      
       }  
     }  
     return result;  
   }  
   public boolean dfs(char[][] board,String word,int startX,int startY,int curC)  
   {  
     if(curC==word.length())  
       return true;  
     if(startX>=board.length||startX<0)  
       return false;  
     if(startY>=board[0].length||startY<0)  
       return false;  
     if(board[startX][startY]!=word.charAt(curC))  
       return false;  
     char tmp=board[startX][startY];  
     board[startX][startY]='#';  
     boolean result=dfs(board,word,startX+1,startY,curC+1)||  
     dfs(board,word,startX-1,startY,curC+1)||  
     dfs(board,word,startX,startY+1,curC+1)||  
     dfs(board,word,startX,startY-1,curC+1);  
     board[startX][startY]=tmp;  
     return result;  
   }  
 }  


Sunday, December 28, 2014

Construct Binary Tree from Inorder and Postorder Traversal (LeetCode Array)

Question:  Given inorder and postorder traversal of a tree, construct the binary tree.
Note:
You may assume that duplicates do not exist in the tree.

Idea: The last element of the postorder is the root. So we construct the tree from the end to the beginning of the postorder traversal in the root, root.right, root.left sequence. In the inorder traversal, we can search the root's position, assume the position is x. The the right half (x+1->end) is the right substree, which is the root of root.right. The same for the left substree. Keep constructing the substrees recursively.

Time: O(n^2) (the search root's position takes O(n), for each of the node).
Space: O(lgn)

Code:
 public class Solution {  
   private int postIndex;  
   public TreeNode buildTree(int[] inorder, int[] postorder) {  
     postIndex=postorder.length-1;  
     return dfs(inorder,postorder,0,inorder.length-1);  
   }  
   public int findIndex(int[] arr,int start,int end,int target)  
   {  
     for(int i=start;i<=end;i++)  
     {  
       if(arr[i]==target)  
         return i;  
     }  
     return -1;  
   }  
   public TreeNode dfs(int[] inorder,int[] postorder,int inStart,int inEnd)  
   {  
     if(inStart>inEnd)  
       return null;  
     TreeNode root=new TreeNode(postorder[postIndex]);  
     int splitPos=findIndex(inorder,inStart,inEnd,root.val);  
     postIndex--;  
     root.right=dfs(inorder,postorder,splitPos+1,inEnd);  
     root.left=dfs(inorder,postorder,inStart,splitPos-1);  
     return root;  
   }  
 }  


Construct Binary Tree from Preorder and Inorder Traversal (LeetCode Array)

Question:  Given preorder and inorder traversal of a tree, construct the binary tree.
Note:
You may assume that duplicates do not exist in the tree.

Idea: From the preorder traversal, we can know the root of the tree. Then we find the root in the inorder traversal, assumes the root position is at index x. Split the inorder traversal into two arrays: (start->x-1) and (x+1->end). The first array is the left substree of root, the second array is the right substree of root. Then construct substrees recursively on the two substrings.

The above is the basic idea. During the implementation, in order to avoid copying the whole string during the split, we only pass the index, e.g. start->x-1, x+1->end.

Time: O(n^2) (find root position in inorder is O(n), for n tree nodes)
Space: O(lgn)

Code: 
 public class Solution {  
   public int preIndex;  
   public TreeNode buildTree(int[] preorder, int[] inorder) {  
     preIndex=0;  
     return buildRec(preorder,inorder,0,inorder.length-1);  
   }  
   public TreeNode buildRec(int[] preorder,int[] inorder,int inStart,int inEnd)  
   {  
     if(inStart>inEnd)  
       return null;  
     TreeNode root=new TreeNode(preorder[preIndex]);  
     preIndex+=1;  
     int splitPos=findIndex(inorder,inStart,inEnd,root.val);  
     root.left=buildRec(preorder,inorder,inStart,splitPos-1);  
     root.right=buildRec(preorder,inorder,splitPos+1,inEnd);  
     return root;  
   }  
   public int findIndex(int[] arr, int start, int end,int target)  
   {  
     for(int i=start;i<=end;i++)  
     {  
       if(arr[i]==target)  
         return i;  
     }  
     return -1;  
   }  
 }