Friday, August 28, 2015

Leetcode: Binary Tree Right Side View

Given a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom.
For example:
Given the following binary tree,
   1            <---
 /   \
2     3         <---
 \     \
  5     4       <---
You should return [1, 3, 4].
Idea:
Obviously, the question asks for a BFS. The major difference from the traditional BFS is it only prints the right-most nodes for each level. Consequently, the basic idea is to add right child before adding the left child. For each level, only print out the first node, which must be the right-most node. 

Code (Java):
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        if (root == null) {
            return result;
        }
        
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode curr = queue.poll();
                if (i == 0) {
                    result.add(curr.val);
                }
                if (curr.right != null) {
                    queue.offer(curr.right);
                }
                
                if (curr.left != null) {
                    queue.offer(curr.left);
                }
            }
        }
        
        return result;
    }
}

A DFS solution: 
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        
        if (root == null) {
            return result;
        }
        
        rightSideViewHelper(root, 0, result);
        
        return result;
    }
    
    private void rightSideViewHelper(TreeNode root, int level, List<Integer> result) {
        if (root == null) {
            return;
        }
        
        if (level == result.size()) {
            result.add(root.val);
        }
        
        rightSideViewHelper(root.right, level + 1, result);
        rightSideViewHelper(root.left, level + 1, result);
    }
}

Tuesday, August 18, 2015

Leetcode: Number of Islands

Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
Example 1:
11110
11010
11000
00000
Answer: 1
Example 2:
11000
11000
00100
00011
Answer: 3
Understand the problem:
This problem is an alternative view of connected components in a undirected graph. It could be easily solved by using either DFS or BFS. 

DFS Solution (Java):
public class Solution {
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0 || grid[0].length == 0) {
            return 0;
        }
        int rows = grid.length;
        int cols = grid[0].length;
        
        boolean[][] visited = new boolean[rows][cols];
        int result = 0;
        
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (grid[i][j] == '1' && !visited[i][j]) {
                    result++;
                    numIslandsHelper(grid, visited, i, j, rows, cols);
                }
            }
        }
        
        return result;
    }
    
    private void numIslandsHelper(char[][] grid, boolean[][] visited, int i, int j, int numRows, int numCols) {
        if (i < 0 || i >= numRows) {
            return;
        }
        
        if (j < 0 || j >= numCols) {
            return;
        }
        
        if (visited[i][j]) {
            return;
        }
        
        // If water
        if (grid[i][j] == '0') {
            return;
        }
        
        // Mark the visted[i][j] = true
        visited[i][j] = true;
        
        // Go up, down, left and right
        numIslandsHelper(grid, visited, i - 1, j, numRows, numCols);
        numIslandsHelper(grid, visited, i + 1, j, numRows, numCols);
        numIslandsHelper(grid, visited, i, j - 1, numRows, numCols);
        numIslandsHelper(grid, visited, i, j + 1, numRows, numCols);
    }
}

A BFS Solution:
public class Solution {
    private Queue<Integer> queue = new LinkedList();
    
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0 || grid[0].length == 0) {
            return 0;
        }
        int rows = grid.length;
        int cols = grid[0].length;
        boolean[][] visited = new boolean[rows][cols];
        
        int result = 0;
        
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (grid[i][j] == '1' && !visited[i][j]) {
                    result++;
                    numIslandsHelper(grid, visited, i, j, rows, cols);
                }
            }
        }
        return result;
    }
    
    private void numIslandsHelper(char[][] grid, boolean[][] visited, int i, int j, int numRows, int numCols) {
        fill(grid, visited, i, j, numRows, numCols);
        
        while (!queue.isEmpty()) {      
            int cord = queue.poll();
            int x = cord / numCols;
            int y = cord % numCols;
    
            fill(grid, visited, x - 1, y, numRows, numCols);
            fill(grid, visited, x + 1, y, numRows, numCols);
            fill(grid, visited, x, y - 1, numRows, numCols);
            fill(grid, visited, x, y + 1, numRows, numCols);
        }
    }
    
    private void fill(char[][] grid, boolean[][] visited, int i, int j, int numRows, int numCols) {
        if (i < 0 || i >= numRows || j < 0 || j >= numCols) {
            return;
        }
        
        if (visited[i][j] || grid[i][j] == '0') {
            return;
        }
        
        visited[i][j] = true;
        
        queue.offer(i * numCols + j);
    }
}

Update on 1/25/16:
Union-find Solution:
public class Solution {
    private int[][] dir = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }
        
        int m = grid.length;
        int n = grid[0].length;
        
        int[] parents = new int[m * n];
        int count = 0;
        
        // Step 1: initialize each node, for each's parent is itself
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                int id = i * n + j;
                parents[id] = id;
            }
        }
        
        // step 2: iterate each node, and connect the neighbors
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == '0') {
                    continue;
                }
                
                for (int[] row : dir) {
                    int x = i + row[0];
                    int y = j + row[1];
                    if (isValid(grid, x, y)) {
                        int p = i * n + j;
                        int q = x * n + y;
                        connect(parents, p, q);
                    }
                }
            }
        }
        
        // step 3: count the number of cc
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                int id = i * n + j;
                if (grid[i][j] == '1' && parents[id] == id) {
                    count++;
                }
            }
        }
        
        return count;
    }
    
    private boolean isValid(char[][] grid, int x, int y) {
        int m = grid.length;
        int n = grid[0].length;
        
        return x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == '1';
    }
    
    private void connect(int[] parents, int p, int q) {
        int pRoot = find(parents, p);
        int qRoot = find(parents, q);
        
        if (pRoot == qRoot) {
            return;
        }
        
        parents[pRoot] = qRoot;
    }
    
    private int find(int[] parents, int id) {
        while (parents[id] != id) {
            id = parents[id];
        }
        
        return id;
    }
}

Updata on 4/21/19:
public class Solution {
    /**
     * @param grid: a boolean 2D matrix
     * @return: an integer
     */
    public int numIslands(boolean[][] grid) {
        // write your code here
        if (grid == null || grid.length == 0) {
            return 0;
        }
        
        int nRows = grid.length;
        int nCols = grid[0].length;
        
        int[] parents = new int[nRows * nCols];
        for (int i = 0; i < parents.length; i++) {
            parents[i] = i;
        }
        
        for (int i = 0; i < nRows; i++) {
            for (int j = 0; j < nCols; j++) {
                if (grid[i][j]) {
                    union(grid, i, j, parents);
                }
            }
        }
        
        int numIslands = 0;
        
        for (int i = 0; i < parents.length; i++) {
            int nx = i / nCols;
            int ny = i % nCols;
            
            if (grid[nx][ny] && parents[i] == i) {
                numIslands++;
            }
        }
        
        return numIslands;
    }
    
    private void union(boolean[][] grid, int x, int y, int[] parents) {
        int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
        
        int nRows = grid.length;
        int nCols = grid[0].length;
        
        for (int i = 0; i < 4; i++) {
            int nx = x + dirs[i][0];
            int ny = y + dirs[i][1];
            
            if (nx >= 0 && nx < nRows && ny >= 0 && ny < nCols && grid[nx][ny]) {
                int myParrent = find(parents, x * nCols + y);
                int neighborParrent = find(parents, nx * nCols + ny);
                
                if (myParrent != neighborParrent) {
                    parents[myParrent] = neighborParrent;
                }
            }
        }
    }
    
    private int find(int[] parents, int x) {
        int root = x;
        while (parents[root] != root) {
            root = parents[root];
        }
        
        // path compression
        //
        while (x != root) {
            int temp = parents[x];
            parents[x] = root;
            x = temp;
        }
        
        return root;
    }
}

Tuesday, October 28, 2014

Leetcode: Surrounded Regions

Given a 2D board containing 'X' and 'O', capture all regions surrounded by 'X'.
A region is captured by flipping all 'O's into 'X's in that surrounded region.
For example,
X X X X
X O O X
X X O X
X O X X
After running your function, the board should be:
X X X X
X X X X
X X X X
X O X X

Understand the problem:
The marginal 'O' cannot be surrounded. In addition, the adjacent 'O' to the marginal 'O' cannot be surrounded as well. So We can mark those 'O' as the other characters, like 'D'. Then mark the other 'O's as 'X', and mark 'D' as 'O'. 

Recursive Solution:
We start from the marginal points, and if it is 'O', we check its neighbored points. 

Code (Java):
public class Solution {
    public void solve(char[][] board) {
        if (board == null || board.length == 0) {
            return;
        }
        
        int m = board.length;
        int n = board[0].length;
        
        // check the first and last rows
        for (int i = 0; i < n; i++) {
            solveHelper(0, i, m, n, board);
            solveHelper(m - 1, i, m, n, board);
        }
        
        for (int i = 1; i < m - 1; i++) {
            solveHelper(i, 0, m, n, board);
            solveHelper(i, n - 1, m, n, board);
        }
        
        // fill
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (board[i][j] == 'O') {
                    board[i][j] = 'X';
                } else if (board[i][j] == 'D') {
                    board[i][j] = 'O';
                }
            }
        }
    }
    
    private void solveHelper(int row, int col, int m, int n, char[][] board) {
        if (row < 0 || row >= m || col < 0 || col >= n || board[row][col] != 'O') {
            return;
        }
        
        board[row][col] = 'D';
        
        solveHelper(row - 1, col, m, n, board);
        solveHelper(row + 1, col, m, n, board);
        solveHelper(row, col - 1, m, n, board);
        solveHelper(row, col + 1, m, n, board);
        
    }
}

BFS Solution:
public class Solution {
    Queue<Integer> queue = new LinkedList<Integer>();
    public void solve(char[][] board) {
        if (board == null || board.length == 0) {
            return;
        }
        
        int m = board.length;
        int n = board[0].length;
        
        for (int i = 0; i < n; i++) {
            solveHelper(0, i, m, n, board);
            solveHelper(m - 1, i, m, n, board);
        }
        
        for (int i = 1; i < m - 1; i++) {
            solveHelper(i, 0, m, n, board);
            solveHelper(i, n - 1, m, n, board);
        }
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (board[i][j] == 'O') {
                    board[i][j] = 'X';
                } else if (board[i][j] == 'D') {
                    board[i][j] = 'O';
                }
            }
        }
    }
    
    private void solveHelper(int row, int col, int m, int n, char[][] board) {
        fill(row, col, m, n, board);
        
        while (!queue.isEmpty()) {
            int cord = queue.poll();
            int x = cord / n;
            int y = cord % n;
            
            fill(x - 1, y, m, n, board);
            fill(x + 1, y, m, n, board);
            fill(x, y - 1, m, n, board);
            fill(x, y + 1, m, n, board);
        }
    }
    
    private void fill(int row, int col, int m, int n, char[][] board) {
        if (row < 0 || row >= m || col < 0 || col >= n || board[row][col] != 'O') {
            return;
        } 
        
        board[row][col] = 'D';
        
        queue.offer(row * n + col);
    }
}

Thursday, August 21, 2014

Leetcode: Binary Tree Zigzag Level Order Traversal

Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to right, then right to left for the next level and alternate between).
For example:
Given binary tree {3,9,20,#,#,15,7},
    3
   / \
  9  20
    /  \
   15   7
return its zigzag level order traversal as:
[
  [3],
  [20,9],
  [15,7]
]
confused what "{1,#,2,3}" means? > read more on how binary tree is serialized on OJ.
Understand the Problem:
The problem can be still categorized into BFS problem. The only difference between the regular BFS is it traversed the tree in Zigzag order. 

Recursive Solution:
We can still solve this problem using the regular BFS solution. However, for the even level, we store the nodes at reverse order. At the odd level, we store at the forward order. Then there is no difference between regular BFS and zigZag BFS.

Code (Java):
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ArrayList<ArrayList<Integer>> zigzagLevelOrder(TreeNode root) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        
        zigzagLevelOrder(root, result, 1);
        
        return result;
    }
    
    private void zigzagLevelOrder(TreeNode root, ArrayList<ArrayList<Integer>> result, int level) {
        if (root == null) return;
        
        if (result.size() < level) {
            ArrayList<Integer> levelList = new ArrayList<Integer>();
            if ((level % 2) == 1) {
                levelList.add(root.val);
            } else {
                levelList.add(0, root.val);
            }
            result.add(levelList);
        } else {
            if ((level % 2) == 1) {
                result.get(level - 1).add(root.val);
            } else {
                result.get(level - 1).add(0, root.val);
            }
        }
        
        zigzagLevelOrder(root.left, result, level + 1);
        zigzagLevelOrder(root.right, result, level + 1);
    }
}

Discussion:
The time complexity for the recursive solution is still O(n^2) since inserting the node at head in the even levels takes O(n) time instead of O(1). 

Iterative Solution:
The iterative solution is very similar to the iterative BFS solution. There are two major difference in order do address this zigzag BFS. One is to save the level information, because we wanna store the node in backward order for even levels. Second, for the even level, we may still insert the nodes at head, as the recursive solution. Or we may employ a stack, and for the dequeued elements from the queue, we push into the stack. At last we pop all elements and append to the array list.

Code (Java):
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ArrayList<ArrayList<Integer>> zigzagLevelOrder(TreeNode root) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        if (root == null) return result;
        
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        int level = 0;
        
        queue.offer(root);
        
        while(!queue.isEmpty()) {
            int size = queue.size();
            level++;
            ArrayList<Integer> levelList = new ArrayList<Integer>();
            Stack<Integer> levelStack = new Stack<Integer>();
            
            for (int i = 0; i < size; i++) {
                TreeNode curr = queue.poll();
                if (level % 2 == 1) {
                    levelList.add(curr.val);
                } else {
                    levelStack.push(curr.val);
                }
                
                if (curr.left != null) {
                    queue.offer(curr.left);
                }
                
                if (curr.right != null) {
                    queue.offer(curr.right);
                }
            }
            
            // for even level, dump the elments from the stack to the arraylist
            if (level % 2 == 0) {
                while (!levelStack.isEmpty()) {
                    levelList.add(levelStack.pop());
                }
            }
            result.add(levelList);
        }
        
        return result;
    }
}


Discussion:
Since pushing and popping an element from the stack takes O(1) time, the overall time complexity is O(n). For the space complexity, since we employed a queue and stack to store the intermediate result, it takes space of O(n) + O(n) = O(n). So the space complexity is still O(n).

Summary:
So far you should be very familiar with all DFS and BFS traversal problems. The recursive solutions are straight-forward, but tricky to understand every detail. Be familiar with the iterative solution, and how queue, stack and pointers are used.

Update on 9/30/14:
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        if (root == null) {
            return result;
        }
        
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        
        while(!queue.isEmpty()) {
            int size = queue.size();
            List<Integer> levelList = new ArrayList<Integer>();
            if (result.size() % 2 == 0) {
                for (int i = 0; i < size; i++) {
                    TreeNode curr = queue.poll();
                    levelList.add(curr.val);
                    
                    if (curr.left != null) {
                        queue.offer(curr.left);
                    }
                    
                    if (curr.right != null) {
                        queue.offer(curr.right);
                    }
                }
            } else {
                Stack<Integer> stack = new Stack<Integer>();
                for (int i = 0; i < size; i++) {
                    TreeNode curr = queue.poll();
                    stack.push(curr.val);
                    
                    if (curr.left != null) {
                        queue.offer(curr.left);
                    }
                    
                    if (curr.right != null) {
                        queue.offer(curr.right);
                    }
                }
                
                while(!stack.isEmpty()) {
                    levelList.add(stack.pop());
                }
            }
            result.add(levelList);
        }
        
        return result;
    }
}

Wednesday, August 20, 2014

Leetcode: Binary Tree Level Order Traversal II

Given a binary tree, return the bottom-up level order traversal of its nodes' values. (ie, from left to right, level by level from leaf to root).
For example:
Given binary tree {3,9,20,#,#,15,7},
    3
   / \
  9  20
    /  \
   15   7
return its bottom-up level order traversal as:
[
  [15,7],
  [9,20],
  [3]
]
confused what "{1,#,2,3}" means? > read more on how binary tree is serialized on OJ.


Understand the problem:
The problem asks for traverse the binary tree in order. The mainly difference between the BFS is it asks for returning the result bottom-up.


Naive Solution:
One native solution is to still use the pre-order traversal of the tree, and save the level, like how we solved the BFS problem recursively. The major difference is instead of appending the list of each level, we add them at front of the result list, and move others forward. 

Code (Java):
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ArrayList<ArrayList<Integer>> levelOrderBottom(TreeNode root) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        
        levelOrderBottom(root, result, 1);
        
        return result;
    }
    
    private void levelOrderBottom(TreeNode root, ArrayList<ArrayList>Integer>> result, int level) {
        if (root == null) return;
        
        if (result.size() < level) {
            ArrayList<Integer> levelList = new ArrayList<Integer>();
            levelList.add(root.val);
            result.add(0, levelList);
        } else {
            result.get(result.size() - level).add(root.val);
        }
        
        levelOrderBottom(root.left, result, level + 1);
        levelOrderBottom(root.right, result, level + 1);
    }
}


Discussion:
Now let's analyze the complexity. Traversing all nodes of the tree takes O(n) time. However, adding the level list at the beginning of the result list and moving the rest behind takes O(n) time as well. So the overall time complexity is O(n^2).

A Better Solution:
Now that we know the time complexity is O(n^2), can we traverse the tree bottom-up, then append it to the array will takes only O(1) time thus the overall time complexity would be O(n). So far I was unabA Better Solution:
Now that we know the time complexity is O(n^2), can we traverse the tree bottom-up, then append it to the array will takes only O(1) time thus the overall time complexity would be O(n). 

So far I was unable to find a method to traverse the tree in level order bottom-up. It is hard to address the following tree
      1
     /  \
   2   3
        /  \
      15  7
If do the post-order traverse, it will start from 2 instead of 15. 

Iterative Solution:
The iterative solution for BFS is to use a queue. The idea is for each node, we enqueue all its left and right children into the queue, and dequeue all of them in next level. 

Code (Java):
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ArrayList<ArrayList<Integer>> levelOrderBottom(TreeNode root) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        if (root == null) return result;
        
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        
        while (!queue.isEmpty()) {
            int size = queue.size();
            ArrayList<Integer> levelList = new ArrayList<Integer>();
            for (int i = 0; i < size; i++) {
                TreeNode curr = queue.poll();
                levelList.add(curr.val);
                
                if (curr.left != null) {
                    queue.offer(curr.left);
                }
                if (curr.right != null) {
                    queue.offer(curr.right);
                }
            }
            result.add(0, levelList);
        }                      
        return result;
    }
} 

Summary:
Note the iterative solution of the BFS, be very familiar with the details. In addition, one possible O(n) solution for the bottom-up traversal is to do the top-down traversal first and get the final result list. Then reverse the list in-place. Since reversing the list takes O(n) time as well, the overall time complexity is still O(n). 

Thursday, August 14, 2014

Leetcode: Word Ladder

Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that:
  1. Only one letter can be changed at a time
  2. Each intermediate word must exist in the dictionary
For example,
Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
Note:
  • Return 0 if there is no such transformation sequence.
  • All words have the same length.
  • All words contain only lowercase alphabetic characters.
Understand the problem:
The question gives two words and a dictionary, find the length of  the shortest transformation sequence from  start to end. Note the two requirements. 

Naive Solution:
One straight-forward solution is to use BFS + a queue. The idea is to add the start into the queue, dequeue, and check if it is one letter difference between the end. If yes, we are done, return the length 2; otherwise, we add all neighbors into the queue. The neighbors are defined as one letter difference between the element just dequeued. Then repeat to check if all its neighbours is one letter difference between the end. If not, add the neighbours again. 
One thing needs to handle is to mark the words in the dictionary as visited. The easiest way is just to remove the visited words in the dictionary. 

Code (Java):
public class Solution {
    public int ladderLength(String start, String end, Set<String> dict) {
        if (start == null || start.isEmpty() || end == null || end.isEmpty())
            return 0;
        
        int length = 0;
        Queue<String> queue = new LinkedList<String>();
        queue.offer(start);
        
        HashSet<String> shadow = new HashSet<String>();
        
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                String curr = queue.poll();
                if (isAdj(curr, end)) {
                    return length + 1;
                } else {
                    for (String str : dict) {
                        if (isAdj(curr, str) && !shadow.contains(str)) {
                            queue.offer(str);
                            //dict.remove(str);
                            shadow.add(str);
                        }
                    }
                    length++;
                }
            }
        }
        return 0;
    }
    
    // determine if two strings have only one different letter
    private boolean isAdj(String str1, String str2) {
        int count = 0;
        for (int i = 0; i < str1.length(); i++) {
            if (str1.charAt(i) != str2.charAt(i)) {
                count++;
            }
        }
        
        return count == 1;
    }
}

Discussion:
Now let's analyze this solution. Note that instead of removing the dictionary, we used a shadow hash set to store the visited nodes. So we don't have to modify the input parameters. For each node dequeued in the queue, it takes O(n) time to find its neighbor. Since there will be totally n nodes enqueued into the queue, it takes totally O(n^2) time. Also consider the complexity of determining if two strings are adjacent, which has time of O(m), where m is the length of the string. The total time complexity is O(n^2 * m). The space complexity is O(n) + O(n) = O(n). 

A Better Solution:
In the naive solution, for each node which is not adjacent to the end, we look for all its neighbors in the dictionary.   This will take O(n * m) time, where n is the number of strings in the dictionary, and m is the length of a string. If we can do this in constant time, or at least proportional to m, the overall time complexity would be O(n * m). Note that 
  • All words contain only lowercase alphabetic characters.
Does it give us a hint? We can change a character of the string at a time and traverse all possible changes, then compare with the end string. If yes, we are done and simply return the length. Else if the changed string equals to the dictionary, add the string into the queue. Since lowercase alphabetic characters have exactly 26 different characters,  it takes O(26 * m) time to traverse all possible changes for a given string. 

Code (Java):
public class Solution {
    public int ladderLength(String start, String end, Set<String> dict) {
        if (start == null || start.isEmpty() || end == null || end.isEmpty())
            return 0;
        
        int length = 1;
        Queue<String> queue = new LinkedList<String>();
        queue.offer(start);
        
        HashSet<String> visited = new HashSet<String>();
        
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                String curr = queue.poll();
                for (int j = 0; j < curr.length(); j++) {
                    char[] charCurr = curr.toCharArray();
                    for (char c = 'a'; c < 'z'; c++) {
                        charCurr[j] = c;  // change one character at a time
                        String strCurr = new String(charCurr);
                        if (strCurr.equals(end)) {
                            return length + 1;
                        } else {
                            if (dict.contains(strCurr) && !visited.contains(strCurr)) {
                                queue.offer(strCurr);
                                visited.add(strCurr);
                            }
                        }
                    }
                }
            }
            length++;
        }
        return 0;
    }
}

Discussion:
Note the line  17 should be in the outer loop, since we can change at most a character at a time. So if we traversed all possible characters for a position, we should recover it back to the original string. 

As we discussed before, the time complexity is O(n * m). The space complexity is O(n). 

Summary:
This question can be categorized into the graph theory, where each node represents a word, and each edge connects two neighbors. For this question, it actually asks us to find the shortest path in a graph. In such case a BFS would be the best solution. Also note that BFS will usually come with a queue. 

Leetcode: Binary Tree Level Order Traversal

Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level).
For example:
Given binary tree {3,9,20,#,#,15,7},
    3
   / \
  9  20
    /  \
   15   7
return its level order traversal as:
[
  [3],
  [9,20],
  [15,7]
]
confused what "{1,#,2,3}" means? > read more on how binary tree is serialized on OJ.


Understand the problem:
The question asks for a BFS, i.e, level order traversal of a binary tree. Note that the format of the output should represent t he layer information, which is a list of a list. 

Recursive Solution:
The recursive solution is very similar to DFS. The difference is we should store the layer information for each node. We can use the preorder traversal. For each level, we check if this level exists in the final result list, if not, create a new list; else, append the node into the corresponding list.

Code (Java):
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        
        levelOrder(root, result, 1);
        
        return result;
    }
    
    private void levelOrder(TreeNode node, ArrayList<ArrayList<Integer>> result, int level) {
        if (node == null) return;
        
        if (result.size() < level) {
            ArrayList<Integer> temp = new ArrayList<Integer>();
            temp.add(node.val);
            result.add(temp);
        } else {
            result.get(level - 1).add(node.val);
        }
        
        levelOrder(node.left, result, level + 1);
        levelOrder(node.right, result, level + 1);
    }
}

Discussion:
The time complexity is O(n) since we traverse all elements of the tree. The space complexity is O(logn) since the recursive preorder traversal.

Iterative Solution:
In the iterative solution, we use a queue. For each element traversed, we enqueue both its left child and right child. Then we enter into the next level and dequeue all the elements of the queue.

Code (Java):
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        if (root == null) return result;
        
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            ArrayList<Integer> levelList = new ArrayList<Integer>();
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode curr = queue.poll();
                levelList.add(curr.val);
                if (curr.left != null) {
                    queue.offer(curr.left);
                }
                if (curr.right != null) {
                    queue.offer(curr.right);
                }
            }
            result.add(levelList);
        }
        return result;
    }
}

Discussion:
The time complexity of the iterative solution is O(n). The space complexity is a bit of tricky. Suppose we have a full tree, so the bottom level of the tree (leaves) has the most number of nodes. Since we use a queue to store the nodes at each level, at each level, we dequeued all nodes at current level, and enqueued nodes for the next level. So the maximal number of elements in the queue is the number of leaves. Suppose the height of the tree is h, we know that the number of leaves is 2^(h-1). Since h = logn, so the number of leaves is 1/2 n. So the number of elements in the queue is bounded by 1/2 O(n), which equals to O(n).

Summary:
So far we learned all basic tree traversal approaches, DFS and BFS. The recursive BFS is a bit of tricky, which is still DFS but store the level information. The iterative BFS is easier to understand. Remember the use of the queue.