算法题解之图论与搜索

Additive Number

加法数

思路:一开始以为要用DP来做,但是这道题除非能保证每个相加数的划分是唯一的(事实上不是唯一的),否则只能用搜索。      

 1 public class Solution {
 2     public boolean isAdditiveNumber(String num) {
 3         for (int firstEnd = 0; firstEnd <= num.length() - 3; firstEnd++) {
 4             if (num.charAt(0) == '0' && firstEnd > 0) {
 5                 break;
 6             }
 7             for (int secondEnd = firstEnd + 1; secondEnd <= num.length() - 2; secondEnd++) {
 8                 if (num.charAt(firstEnd + 1) == '0' && secondEnd > firstEnd + 1) {
 9                     break;
10                 }
11                 long first = Long.parseLong(num.substring(0, firstEnd + 1));
12                 long second = Long.parseLong(num.substring(firstEnd + 1, secondEnd + 1));
13                 if (helper(num, secondEnd + 1, first + second, second)) {
14                     return true;
15                 }
16             }
17         }
18         return false;
19     }
20     
21     public boolean helper(String num, int start, long expect_num, long pre_num) {
22         if (start > num.length() - 1) {
23             return true;
24         }
25         if (num.charAt(start) == '0' && expect_num != 0) {
26             return false;
27         }
28         int end = start + String.valueOf(expect_num).length() - 1;
29         if (end + 1 > num.length()) {
30             return false;
31         }
32         long cur = Long.parseLong(num.substring(start, end + 1));
33         if (cur == expect_num) {
34             long next_expect = pre_num + cur;
35             return helper(num, end + 1, next_expect, cur);
36         }
37         return false;
38     }
39 }
View Code

 

 

Clone Graph

克隆图

思路:类似于copy random list,先克隆每个节点的值,建立新老节点的hash映射,再根据映射关系克隆邻接表。

/**
 * Definition for undirected graph.
 * class UndirectedGraphNode {
 *     int label;
 *     ArrayList<UndirectedGraphNode> neighbors;
 *     UndirectedGraphNode(int x) { label = x; neighbors = new ArrayList<UndirectedGraphNode>(); }
 * };
 */
public class Solution {
    /**
     * @param node: A undirected graph node
     * @return: A undirected graph node
     */
    public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
        // write your code here
        if (node == null) {
            return null;
        }
        Map<UndirectedGraphNode, UndirectedGraphNode> old2new 
                = new HashMap<UndirectedGraphNode, UndirectedGraphNode>();
        Queue<UndirectedGraphNode> q = new LinkedList<UndirectedGraphNode>();
        
        q.offer(node);
        UndirectedGraphNode copy = new UndirectedGraphNode(node.label);
        copy.neighbors = new ArrayList<UndirectedGraphNode>(node.neighbors);
        old2new.put(node, copy);
        while (!q.isEmpty()) {
            int size = q.size();
            for (int i = 1; i <= size; i++) {
                UndirectedGraphNode cur = q.poll();
                for (UndirectedGraphNode next : cur.neighbors) {
                    if (!old2new.containsKey(next)) {
                        q.offer(next);
                        UndirectedGraphNode copyNode = new UndirectedGraphNode(next.label);
                        copyNode.neighbors = new ArrayList<UndirectedGraphNode>(next.neighbors);
                        old2new.put(next, copyNode);
                    }
                }
            }
        }
        
        for (Map.Entry<UndirectedGraphNode, UndirectedGraphNode> entry : old2new.entrySet()) {
            UndirectedGraphNode copy2 = entry.getValue();
            ArrayList<UndirectedGraphNode> neighbors = copy2.neighbors; 
            for (int i = 0; i < neighbors.size(); i++) {
                neighbors.set(i, old2new.get(neighbors.get(i)));
            }
        }
        
        return old2new.get(node);
    }
}
View Code

 

 

Course Schedule

课程计划

思路:这道题的本质其实是判断一张图是否存在拓扑排序。拓扑排序表示的是修课的顺序。根据边集合构造图,然后就是拓扑排序那一套了。

 1 public class Solution {
 2     public boolean canFinish(int numCourses, int[][] prerequisites) {
 3         Map<Integer, Value> map = new HashMap<Integer, Value>();
 4         for (int i = 0; i < prerequisites.length; i++) {
 5             int to = prerequisites[i][0];
 6             int from = prerequisites[i][1];
 7             
 8             if (!map.containsKey(from)) {
 9                 map.put(from, new Value());
10             }
11             if (!map.containsKey(to)) {
12                 map.put(to, new Value());
13             }
14             map.get(from).neighbors.add(to);
15             map.get(to).inEdges += 1; 
16         }
17         
18         
19         Queue<Integer> q = new LinkedList<Integer>();
20         for (Integer course : map.keySet()) {
21             if (map.get(course).inEdges == 0) {
22                 q.offer(course);
23             }
24         }
25         int notFinished = map.size();
26         while (!q.isEmpty()) {
27             int finish = q.poll();
28             notFinished--;
29             for (Integer course : map.get(finish).neighbors) {
30                 if (--map.get(course).inEdges == 0) {
31                     q.offer(course);   
32                 }
33             }
34         }
35         if (notFinished == 0) {
36             return true;
37         } else {
38             return false;
39         }
40     }
41     
42 }
43 
44 class Value {
45     int inEdges;
46     List<Integer> neighbors;
47     Value() {
48         neighbors = new ArrayList<Integer>();
49     }
50 }
View Code

 

 

Course Schedule II

课程计划II

思路:本质就是求拓扑排序。

 1 public class Solution {
 2     public int[] findOrder(int numCourses, int[][] prerequisites) {
 3         Map<Integer, Value> map = new HashMap<Integer, Value>();
 4         for (int i = 0; i < prerequisites.length; i++) {
 5             int to = prerequisites[i][0];
 6             int from = prerequisites[i][1];
 7             
 8             if (!map.containsKey(from)) {
 9                 map.put(from, new Value());
10             }
11             if (!map.containsKey(to)) {
12                 map.put(to, new Value());
13             }
14             map.get(from).neighbors.add(to);
15             map.get(to).inEdges += 1; 
16         }
17         
18         Queue<Integer> q = new LinkedList<Integer>();
19         for (Integer course : map.keySet()) {
20             if (map.get(course).inEdges == 0) {
21                 q.offer(course);
22             }
23         }
24         
25         int[] res = new int[numCourses];
26         int cur = 0;
27         while (!q.isEmpty()) {
28             int finish = q.poll();
29             res[cur++] = finish;
30             for (Integer course : map.get(finish).neighbors) {
31                 if (--map.get(course).inEdges == 0) {
32                     q.offer(course);   
33                 }
34             }
35         }
36         if (cur != map.size()) {
37             return new int[0];
38         }
39         
40         for (int i = 0; i < numCourses; i++) {
41             if (!map.containsKey(i)) {
42                 res[cur++] = i;
43             }
44         }
45 
46         return res;
47     }
48     
49 }
50 
51 class Value {
52     int inEdges;
53     List<Integer> neighbors;
54     Value() {
55         neighbors = new ArrayList<Integer>();
56     }
57 }
View Code

 

 

Combination Sum

组合sum

思路:常规DFS题,先排序,再搜索。注意代码中如何去重。

public class Solution {
    /**
     * @param candidates: A list of integers
     * @param target:An integer
     * @return: A list of lists of integers
     */
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        // write your code here
        Arrays.sort(candidates);
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        helper(res, new ArrayList<Integer>(), candidates, 0, 0, target);
        return res;
    }
    
    public void helper(List<List<Integer>> res, List<Integer> cur, 
        int[] candidates, int start, int sum, int target) {
            
        if (sum == target) {
            List<Integer> tmp = new ArrayList<Integer>(cur);
            res.add(tmp);
            return;
        }
        
        for (int i = start; i < candidates.length;) {
            if (sum + candidates[i] > target) {
                break;
            }
            cur.add(candidates[i]);
            helper(res, cur, candidates, i, sum + candidates[i], target);
            cur.remove(cur.size() - 1);
            
            if(i == candidates.length - 1) {
                break;
            }
            while (i < candidates.length - 1 && candidates[i++] == candidates[i]) {}
            if (i == candidates.length - 1 && candidates[i] == candidates[i-1]) {
                i++;
            }
        }
    }
}
View Code

 

 

Find the Connected Component in the Undirected Graph

找无向图的连通块

思路:以图中任意一个节点为起点做搜索,搜索完就找到一个连通块,并把搜索过的节点加入set。再对图中其他节点,如果搜索过就pass,没搜索过就搜索。

/**
 * Definition for Undirected graph.
 * class UndirectedGraphNode {
 *     int label;
 *     ArrayList<UndirectedGraphNode> neighbors;
 *     UndirectedGraphNode(int x) { label = x; neighbors = new ArrayList<UndirectedGraphNode>(); }
 * };
 */
public class Solution {
    /**
     * @param nodes a array of Undirected graph node
     * @return a connected set of a Undirected graph
     */
    public List<List<Integer>> connectedSet(ArrayList<UndirectedGraphNode> nodes) {
        // Write your code here
        Set<UndirectedGraphNode> searchedNodes = new HashSet<UndirectedGraphNode>();
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        for (UndirectedGraphNode node : nodes) {
            if (searchedNodes.contains(node)) {
                continue;                
            }
            List<Integer> cur_res = new ArrayList<Integer>();
            Queue<UndirectedGraphNode> q = new LinkedList<UndirectedGraphNode>();
            Set<UndirectedGraphNode> set = new HashSet<UndirectedGraphNode>();
            q.offer(node);
            set.add(node);
            searchedNodes.add(node);
            while (!q.isEmpty()) {
                int size = q.size();
                for (int i = 1; i <= size; i++) {
                    UndirectedGraphNode cur = q.poll();
                    cur_res.add(cur.label);
                    for (UndirectedGraphNode neighbor : cur.neighbors) {
                        if (!set.contains(neighbor)) {
                            q.offer(neighbor);
                            set.add(neighbor);
                            searchedNodes.add(neighbor);
                        }
                    }
                }
            }
            Collections.sort(cur_res);
            res.add(cur_res);
        }
        return res;
    }
}
View Code

 

 

k Sum II

k数和II

思路:常规的搜索题。注意求方案个数的K sum是用动态规划。

public class Solution {
    /**
     * @param A: an integer array.
     * @param k: a positive integer (k <= length(A))
     * @param target: a integer
     * @return a list of lists of integer 
     */ 
    public ArrayList<ArrayList<Integer>> kSumII(int[] A, int k, int target) {
        // write your code here
        ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
        helper(res, new ArrayList<Integer>(), 0, target, k, 0, A);
        return res;
    }
    
    public void helper(ArrayList<ArrayList<Integer>> res, ArrayList<Integer> cur, 
        int start, int target, int k, int sum, int[] A) {
        
        if (cur.size() == k) {
            if (sum == target) {
                res.add(new ArrayList<Integer>(cur));
            }
            return;
        }
        
        for (int i = start; i < A.length; i++) {
            cur.add(A[i]);
            helper(res, cur, i + 1, target, k, sum + A[i], A);
            cur.remove(cur.size() - 1);
        }
    }
}
View Code

 

 

Minimum Height Trees 

最小高度树

思路:不断去掉外面一层叶子节点(即度为1的点),最后剩下的一个或两个点就是root。原理暂时还不懂。。

 1 public class Solution {
 2     public List<Integer> findMinHeightTrees(int n, int[][] edges) {
 3         List<Integer> res = new ArrayList<Integer>();
 4         if (n == 1) {
 5             res.add(0);
 6             return res;
 7         }
 8         if (n == 2) {
 9             res.add(0);
10             res.add(1);
11             return res;
12         }
13         
14         List<Set<Integer>> map = new ArrayList<Set<Integer>>();
15         for (int i = 0; i < edges.length; i++) {
16             int n1 = edges[i][0];
17             int n2 = edges[i][1];
18             if (map.get(n1) == null) {
19                 map.add(n1, new HashSet<Integer>());
20             }
21             if (map.get(n2) == null) {
22                 map.put(n2, new HashSet<Integer>());
23             }
24             map.get(n1).add(n2);
25             map.get(n2).add(n1);
26         }
27         
28         Queue<Integer> q = new LinkedList<Integer>();
29         for (Integer node : map.keySet()) {
30             if (map.get(node).size() == 1) {
31                 q.offer(node);
32             }
33         }
34         
35         while (!q.isEmpty()) {
36             int size = q.size();
37             for (int i = 1; i <= size; i++) {
38                 int cur = q.poll();
39                 int neighbor = map.get(cur).iterator().next();
40                 map.get(neighbor).remove(cur);
41                 if (map.get(neighbor).size() == 1) {
42                     q.offer(neighbor);
43                 }
44                 map.remove(cur);
45             }
46             if (map.size() <= 2) {
47                 break;
48             }
49         }
50         
51         for (Integer node : map.keySet()) {
52             res.add(node);
53         }
54         return res;
55     }
56 }
View Code

 

 

Number of Islands

小岛数量

思路:遍历每个元素,如果是岛屿并且没有被搜过就BFS这个岛,同时岛屿数加一。 

 1 public class Solution {
 2     public int numIslands(char[][] grid) {
 3         if (grid == null || grid.length == 0 || grid[0].length == 0) {
 4             return 0;
 5         }
 6         int res = 0;
 7         boolean[][] searched = new boolean[grid.length][grid[0].length];
 8         for (int i = 0; i < grid.length; i++) {
 9             for (int j = 0; j < grid[0].length; j++) {
10                 if (grid[i][j] == '1' && !searched[i][j]) {
11                     res++;
12                     searchAndMark(grid, i, j, searched);
13                 }
14             }
15         }
16         return res;
17     }
18     
19     public void searchAndMark(char[][] grid, int cur_x, int cur_y, boolean[][] searched) {
20         
21         if (cur_x >= 0 && cur_x < grid.length && cur_y >= 0 && cur_y < grid[0].length 
22                 && !searched[cur_x][cur_y] && grid[cur_x][cur_y] == '1') {
23             searched[cur_x][cur_y] = true;
24             searchAndMark(grid, cur_x - 1, cur_y, searched);
25             searchAndMark(grid, cur_x, cur_y - 1, searched);
26             searchAndMark(grid, cur_x + 1, cur_y, searched);
27             searchAndMark(grid, cur_x, cur_y + 1, searched);
28         }
29     }
30 }
View Code

 

 

N-Queens

N皇后

思路:常规DFS。使用三个boolean数组分别记录每一列,每一条主对角线(i-j相等),每一条副对角线(i+j相等)是否能放。再用一个字符数组board表示当前的棋牌放置。

class Solution {
    /**
     * Get all distinct N-Queen solutions
     * @param n: The number of queens
     * @return: All distinct solutions
     * For example, A string '...Q' shows a queen on forth position
     */
    ArrayList<ArrayList<String>> solveNQueens(int n) {
        // write your code here
        char[][] board = new char[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                board[i][j] = '.';
            }
        }
        
        boolean[] col_used = new boolean[n];
        boolean[] pos_diag_used = new boolean[2 * n]; 
        boolean[] neg_diag_used = new boolean[2 * n];
        ArrayList<ArrayList<String>> res = new ArrayList<ArrayList<String>>();
        dfs(res, board, col_used, pos_diag_used, neg_diag_used, 0);
        return res;
    }
    
    public void dfs(ArrayList<ArrayList<String>> res, char[][] board, 
        boolean[] col_used, boolean[] pos_diag_used, boolean[] neg_diag_used,
        int cur_row) {
        int n = board.length;
        if (cur_row == n) {
            ArrayList<String> curSolution = buildRes(board);
            res.add(curSolution);
            return;
        }
        
        for (int j = 0; j < n; j++) {
            int pos_diag_index = (cur_row - j >= 0) ? cur_row - j  : 
                n - 1 + j - cur_row;
            if (col_used[j] == false && pos_diag_used[pos_diag_index] == false &&
                neg_diag_used[cur_row + j] == false) {
                
                col_used[j] = true; 
                pos_diag_used[pos_diag_index] = true;
                neg_diag_used[cur_row + j] = true;
                board[cur_row][j] = 'Q';
                dfs(res, board, col_used, pos_diag_used, neg_diag_used, cur_row + 1);
                col_used[j] = false; 
                pos_diag_used[pos_diag_index] = false;
                neg_diag_used[cur_row + j] = false;
                board[cur_row][j] = '.';
            }
        }
    }
    
    public ArrayList<String> buildRes(char[][] board) {
        ArrayList<String> solution = new ArrayList<String>();
        for (int i = 0; i < board.length; i++) {
            solution.add(String.valueOf(board[i]));
        }
        return solution;
    }
};
View Code

 

 

N-Queens II

N皇后II

思路:常规DFS搜索题。使用三个boolean数组分别记录每一列,每一条主对角线(i-j相等),每一条副对角线(i+j相等)是否能放。

class Solution {
    /**
     * Calculate the total number of distinct N-Queen solutions.
     * @param n: The number of queens.
     * @return: The total number of distinct solutions.
     */
    int solutionCount = 0;
    public int totalNQueens(int n) {
        //write your code here
        boolean[] col_Used = new boolean[n];
        boolean[] diag_pos_used = new boolean[2 * n]; 
        boolean[] diag_neg_used = new boolean[2 * n]; 
        dfs(col_Used, diag_pos_used, diag_neg_used, 0, n);
        return solutionCount;
    }
    
    public void dfs(boolean[] col_Used, boolean[] diag_pos_used, 
        boolean[] diag_neg_used, int cur_row, int n) {
        
        if (cur_row == n) {
            solutionCount++;
            return;
        }
        for (int i = 0; i < n; i++) {
            int tmp = (cur_row - i) >= 0 ? cur_row - i : n-1+(i-cur_row);  
            if (col_Used[i] == false && diag_pos_used[tmp] == false &&
                diag_neg_used[i+cur_row] == false) {
                col_Used[i] = true;
                diag_pos_used[tmp] = true;
                diag_neg_used[i+cur_row] = true;
                dfs(col_Used, diag_pos_used, diag_neg_used, cur_row + 1, n);
                col_Used[i] = false;
                diag_pos_used[tmp] = false;
                diag_neg_used[i+cur_row] = false;
            }
        }
    }  
}
View Code

 

 

Palindrome Partitioning

分割回文串

思路:时间复杂度是O(2^n),即每个切口切或不切,有2^n-1种切法。先用动态规划求出一个二维数组表示每个字串是否是回文串,再进行DFS。

public class Solution {
    /**
     * @param s: A string
     * @return: A list of lists of string
     */
    public List<List<String>> partition(String s) {
        // write your code here
        int[][] dp = new int[s.length()][s.length()];
        for (int i = 0; i <= s.length() - 1; i++) {
            dp[i][i] = 1;
        }
        for (int i = 0; i <= s.length() - 2; i++) {
            if (s.charAt(i) == s.charAt(i + 1)) {
                dp[i][i+1] = 1;
            }
        }
        for (int len = 3; len <= s.length(); len++) {
            for (int i = 0; i <= s.length() - len; i++) {
                if (s.charAt(i) == s.charAt(i+len-1) && dp[i+1][i+len-2] == 1) {
                    dp[i][i+len-1] = 1;
                }
            }
        }
        
        List<List<String>> res = new ArrayList<List<String>>();
        helper(res, new ArrayList<String>(), s, 0, dp);
        return res;
    }
    
    public void helper(List<List<String>> res, List<String> cur, String s, 
                        int start, int[][] dp) {
        if (start > s.length() - 1) {
            List<String> tmp = new ArrayList<String>(cur);
            res.add(tmp);
            return;
        }
        
        for (int end = start; end <= s.length() - 1; end++) {
            if (dp[start][end] == 1) {
                cur.add(s.substring(start, end + 1));
                helper(res, cur, s, end + 1, dp);
                cur.remove(cur.size() - 1);
            }
        }
    }
}
View Code

 

 

Permutations II

带重复元素的排列

思路:常规DFS。记录一个used数组。如何避免重复: 比如,给出一个排好序的数组,[1,2,2],那么第一个2和第二2如果在结果中互换位置, 我们也认为是同一种方案,所以我们强制要求相同的数字,原来排在前面的,在结果当中也应该排在前面,这样就保证了唯一性。所以当前面的2还没有使用的时 候,就不应该让后面的2使用。

class Solution {
    /**
     * @param nums: A list of integers.
     * @return: A list of unique permutations.
     */
    public List<List<Integer>> permuteUnique(int[] nums) {
        // Write your code here
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        helper(res, new ArrayList<Integer>(), new boolean[nums.length], nums);
        return res;
    }
    
    public void helper(List<List<Integer>> res, List<Integer> cur, 
        boolean[] used, int[] nums) {
        
        if (cur.size() == nums.length) {
            List<Integer> tmp = new ArrayList<Integer>(cur);
            res.add(tmp);
            return;
        }
        
        for (int i = 0; i < nums.length; i++) {
            if (used[i] == false) {
                if (i != 0 && nums[i] == nums[i-1] && used[i-1] == false) {
                    continue;
                } 
                cur.add(nums[i]);
                used[i] = true;
                helper(res, cur, used, nums);
                used[i] = false;
                cur.remove(cur.size() - 1);
            }
        }
    }
}
View Code

 

 

Permutation Sequence

全排列序列

思路:这道题用搜索会超时。放在这是为了与PermutationsI和II对比。这道题可以用O(n)的时间复杂度来求解。

   第一位是数字可能是1~n,其中以每个数字打头的有(n-1)!种全排列,因此第一位数应该是 1 ~ n中的第 k / (n-1)!个数,设为a;

   接着,问题转化为,1~n中去掉a后,剩下的数中第 k%(n - 1)!个全排列,按照找第一位数的方法再来找第二位数,第三位数..........。

 1 public class Solution {
 2     public String getPermutation(int n, int k) {
 3         String res = "";
 4         List<Integer> nums = new ArrayList<Integer>();
 5         for (int i = 1; i <= 9; i++) {
 6             nums.add(i);
 7         }
 8         
 9         //facs[i]表示阶乘i!
10         int[] facs = new int[10];
11         facs[1] = 1;
12         for (int i = 2; i <= 9; i++) {
13             facs[i] = facs[i - 1] * i;
14         }
15         
16         int j = 0;   //j表示这一位数是剩余的数的第j个数
17         int m = n;   //m表示剩下多少个数
18         for (int i = 1; i < n; i++) {
19             int fac = facs[m - 1];
20             j = k % fac == 0 ? k / fac - 1 : k / fac;
21             k = k % fac == 0 ? fac : k % fac;
22             res += String.valueOf(nums.get(j));
23             nums.remove(j);
24             m--;
25         }
26         res += String.valueOf(nums.get(0));
27         return res;
28     }
29 }    
View Code

 

 

Subsets II

带重复元素的子集

思路:常规dfs题目,先排序,后搜索.注意如何去重

class Solution {
    /**
     * @param nums: A set of numbers.
     * @return: A list of lists. All valid subsets.
     */
    public ArrayList<ArrayList<Integer>> subsetsWithDup(int[] nums) {
        // write your code here
        Arrays.sort(nums);
        ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
        
        helper(res, new ArrayList<Integer>(), nums, 0);
        return res;
        
    }
    
    public void helper(ArrayList<ArrayList<Integer>> res, ArrayList<Integer> cur, 
        int[] nums, int start) {
        
        ArrayList<Integer> tmp = new ArrayList<Integer>(cur);
        res.add(tmp);
        
        for (int i = start; i < nums.length;) {
            cur.add(nums[i]);
            helper(res, cur, nums, i + 1);
            cur.remove(cur.size() - 1);
            
            if (i == nums.length - 1) {
                i++;
            }
            while (i < nums.length - 1 && nums[i++] == nums[i]) {}
            if (i == nums.length - 1 && nums[i-1] == nums[i]) {
                i++;
            }
        }
    }
}
View Code

 

 

Six Degrees

六度问题

思路:使用BFS,一层一层搜索,到达目标节点的层数就是答案。

/**
 * Definition for Undirected graph.
 * class UndirectedGraphNode {
 *     int label;
 *     List<UndirectedGraphNode> neighbors;
 *     UndirectedGraphNode(int x) { 
 *         label = x;
 *         neighbors = new ArrayList<UndirectedGraphNode>(); 
 *     }
 * };
 */
public class Solution {
    /**
     * @param graph a list of Undirected graph node
     * @param s, t two Undirected graph nodes
     * @return an integer
     */
    public int sixDegrees(List<UndirectedGraphNode> graph,
                          UndirectedGraphNode s,
                          UndirectedGraphNode t) {
        // Write your code here
        if (s.equals(t)) {
            return 0;
        }
        Set<UndirectedGraphNode> set = new HashSet<UndirectedGraphNode>();
        Queue<UndirectedGraphNode> queue = new LinkedList<UndirectedGraphNode>();
        int layernum = 1;
        
        set.add(s);
        queue.offer(s);
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 1; i <= size; i++) {
                UndirectedGraphNode cur = queue.poll();
                for (int j = 0; j < cur.neighbors.size(); j++) {
                    UndirectedGraphNode next = cur.neighbors.get(j);
                    if (set.contains(next)) {
                        continue;
                    }
                    set.add(next);
                    queue.offer(next);
                    if (next.equals(t)) {
                        return layernum;
                    }
                }
            }
            layernum++;
        }
        return -1;
    }
}
View Code

 

 

Topological Sorting

拓扑排序

思路:先找出任意一个没有入边的点。然后显示出该点,并将它及其边从图中删除。然后对图中其余部分应用这种方法。

   具体来说:先求每个节点的入度,然后把入度为0的节点放入队列中,出队的时候记录节点并将邻接表中所有节点的入度都减1,如果产生新的入度为0的节点就放入队列。直到队列为空。

/**
 * Definition for Directed graph.
 * class DirectedGraphNode {
 *     int label;
 *     ArrayList<DirectedGraphNode> neighbors;
 *     DirectedGraphNode(int x) { label = x; neighbors = new ArrayList<DirectedGraphNode>(); }
 * };
 */
public class Solution {
    /**
     * @param graph: A list of Directed graph node
     * @return: Any topological order for the given graph.
     */    
    public ArrayList<DirectedGraphNode> topSort(ArrayList<DirectedGraphNode> graph) {
        // write your code here
        Map<DirectedGraphNode, Integer> nodeMap = new HashMap<DirectedGraphNode, Integer>();
        for (DirectedGraphNode node : graph) {
            nodeMap.put(node, 0);
        }
        for (DirectedGraphNode node : graph) {
            for (DirectedGraphNode neighbor : node.neighbors) {
                nodeMap.put(neighbor, nodeMap.get(neighbor) + 1);
            }
        }
        
        Queue<DirectedGraphNode> q = new LinkedList<DirectedGraphNode>();
        for (Map.Entry<DirectedGraphNode, Integer> entry : nodeMap.entrySet()) {
            if (entry.getValue() == 0) {
                q.offer(entry.getKey());
            }
        }
        
        ArrayList<DirectedGraphNode> res = new ArrayList<DirectedGraphNode>();
        while (!q.isEmpty()) {
            DirectedGraphNode cur = q.poll();
            res.add(cur);
            for (DirectedGraphNode neighbor : cur.neighbors) {
                nodeMap.put(neighbor, nodeMap.get(neighbor) - 1);
                if (nodeMap.get(neighbor) == 0) {
                    q.offer(neighbor);
                }
            }
        }
        return res;
        
    }
}
View Code

 

 

Word Ladder

单词接龙

思路:第一步:求出每个单词的邻接单词表,即word2neighbors,它的键是dict中每个单词,值是与该单词相差一个单词的所有单词。

     第二步:根据邻接单词表,从end开始作bfs遍历,一层一层遍历,直到找到start,返回当前的层数。

public class Solution {
    /**
      * @param start, a string
      * @param end, a string
      * @param dict, a set of string
      * @return an integer
      */
    public int ladderLength(String start, String end, Set<String> dict) {
        // write your code here
        if (start.equals(end)) {
            return 1;
        }
        dict.add(start);
        dict.add(end);
        int len = start.length();
        Map<String, List<String>> word2neighbors = getNeibors(dict, len);
        return bfs(word2neighbors, start, end);
    }
    
    public Map<String, List<String>> getNeibors(Set<String> dict, int len) {
        Map<String, List<String>> word2neighbors = new HashMap<String, List<String>>();
        for (int i = 0; i < len; i++) {
            Map<String, List<String>> rep2words = new HashMap<String, List<String>>();
            for (String word : dict) {
                String rep = word.substring(0, i) + word.substring(i + 1);
                updateMap(rep2words, rep, word);
            }
            for (Map.Entry<String, List<String>> entry : rep2words.entrySet()) {
                List<String> words = entry.getValue();
                for (String word1 : words) {
                    for (String word2 : words) {
                        if (!word1.equals(word2)) {
                            updateMap(word2neighbors, word1, word2);
                        }
                    }
                }
            }
        }
        return word2neighbors;
    }
    
    public int bfs(Map<String, List<String>> word2neighbors, String start, String end) {
        
        Set<String> searched = new HashSet<String>();
        Queue<String> q = new LinkedList<String>();
        q.offer(start);
        searched.add(start);
        int layer = 1;
        
        while(!q.isEmpty()) {
            int size = q.size();
            for (int i = 1; i <= size; i++) {
                String cur = q.poll();
                List<String> neighbors = word2neighbors.get(cur);
                for (String neighbor : neighbors) {
                    if (!searched.contains(neighbor)) {
                        if (neighbor.equals(end)) {
                            return layer + 1;
                        }
                        q.offer(neighbor);
                        searched.add(neighbor);
                    }
                }
            }
            layer++;
        }
        return -1;
    }
    
    public void updateMap(Map<String, List<String>> map, String key, String value) {
        List<String> list = map.get(key);
        if (list == null) {
            list = new ArrayList<String>();
            list.add(value);
            map.put(key, list);
        } else {
            list.add(value);
        }
    }
}
View Code

 

 

Word Ladder II

单词接龙II

思路1:本题是搜索题目中的较难题。

   第一步:求出每个单词的邻接单词表,即word2neighbors,它的键是dict中每个单词,值是与该单词相差一个单词的所有单词。

     第二步:根据邻接单词表,从end开始作bfs遍历,一层一层遍历,直到找到start。并生成prewords表,它的键是遍历过的每个单词,值是在遍历路径上连到该单词的所有的前驱单词;

     第三步:根据prewords表,从start开始作dfs遍历,遍历到end时就将路径加入res。

public class Solution {
    /**
      * @param start, a string
      * @param end, a string
      * @param dict, a set of string
      * @return a list of lists of string
      */
    public List<List<String>> findLadders(String start, String end, Set<String> dict) {
        // write your code here  
        dict.add(start);
        dict.add(end);
        int len = start.length();
        Map<String, List<String>> word2neighbors = getNeibors(dict, len);
        Map<String, List<String>> prewords = bfs(word2neighbors, start, end);
        
        List<List<String>> res = new ArrayList<List<String>>();
        List<String> cur = new ArrayList<String>();
        cur.add(start);
        dfs(res, cur, prewords, start, end);
        return res;
        
    }
    
    public Map<String, List<String>> getNeibors(Set<String> dict, int len) {
        Map<String, List<String>> word2neighbors = new HashMap<String, List<String>>();
        for (int i = 0; i < len; i++) {
            Map<String, List<String>> rep2words = new HashMap<String, List<String>>();
            for (String word : dict) {
                String rep = word.substring(0, i) + word.substring(i + 1);
                updateMap(rep2words, rep, word);
            }
            for (Map.Entry<String, List<String>> entry : rep2words.entrySet()) {
                List<String> words = entry.getValue();
                for (String word1 : words) {
                    for (String word2 : words) {
                        if (!word1.equals(word2)) {
                            updateMap(word2neighbors, word1, word2);
                        }
                    }
                }
            }
        }
        return word2neighbors;
    }
    
    public Map<String, List<String>> bfs(Map<String, List<String>> word2neighbors, String start, String end) {
        
        Set<String> searched = new HashSet<String>();
        Map<String, List<String>> prewords = 
            new HashMap<String, List<String>>();
        Queue<String> q = new LinkedList<String>();
        q.offer(end);
        searched.add(end);
        
        boolean find = false;
        while(!q.isEmpty()) {
            int size = q.size();
            Set<String> nextLev = new HashSet<String>();
            for (int i = 1; i <= size; i++) {
                String cur = q.poll();
                List<String> neighbors = word2neighbors.get(cur);
                for (String neighbor : neighbors) {
                    if (!searched.contains(neighbor)) {
                        if (neighbor.equals(start)) {
                            find = true;
                        }
                        if (!nextLev.contains(neighbor)) {
                            q.offer(neighbor);
                            nextLev.add(neighbor);
                        }
                        updateMap(prewords, neighbor, cur);
                    }
                }
            }
            if (find) {
                break;
            }
            searched.addAll(nextLev);
        }
        return prewords;
    }
    
    public void dfs(List<List<String>> res, List<String> cur,
        Map<String, List<String>> prewords, String curstring, String end) {
        
        if (curstring.equals(end)) {
            List<String> tmp = new ArrayList<String>(cur);
            res.add(tmp);
            return;
        }
        
        List<String> words = prewords.get(curstring);
        for (String word : words) {
            cur.add(word);
            dfs(res, cur, prewords, word, end);
            cur.remove(cur.size() - 1);
        }
    } 
    
    public void updateMap(Map<String, List<String>> map, String key, String value) {
        List<String> list = map.get(key);
        if (list == null) {
            list = new ArrayList<String>();
            list.add(value);
            map.put(key, list);
        } else {
            list.add(value);
        }
    }
}
View Code

 思路2:(九章算法答案)

   第一步:与思路1一样;

   第二步:从start做bfs,求出所有节点到start的最短距离distance(相当于位于第几层)。

   第三步:从end做dfs,每个节点只往它的邻节点中distance值小1的节点搜索。

public class Solution {
    public List<List<String>> findLadders(String start, String end,
            Set<String> dict) {
        List<List<String>> ladders = new ArrayList<List<String>>();
        Map<String, List<String>> map = new HashMap<String, List<String>>();
        Map<String, Integer> distance = new HashMap<String, Integer>();

        dict.add(start);
        dict.add(end);
 
        bfs(map, distance, start, end, dict);
        
        List<String> path = new ArrayList<String>();
        
        dfs(ladders, path, end, start, distance, map);

        return ladders;
    }

    void dfs(List<List<String>> ladders, List<String> path, String crt,
            String start, Map<String, Integer> distance,
            Map<String, List<String>> map) {
        path.add(crt);
        if (crt.equals(start)) {
            Collections.reverse(path);
            ladders.add(new ArrayList<String>(path));
            Collections.reverse(path);
        } else {
            for (String next : map.get(crt)) {
                if (distance.containsKey(next) && distance.get(crt) == distance.get(next) + 1) { 
                    dfs(ladders, path, next, start, distance, map);
                }
            }           
        }
        path.remove(path.size() - 1);
    }

    void bfs(Map<String, List<String>> map, Map<String, Integer> distance,
            String start, String end, Set<String> dict) {
        Queue<String> q = new LinkedList<String>();
        q.offer(start);
        distance.put(start, 0);
        for (String s : dict) {
            map.put(s, new ArrayList<String>());
        }
        
        while (!q.isEmpty()) {
            String crt = q.poll();

            List<String> nextList = expand(crt, dict);
            for (String next : nextList) {
                map.get(next).add(crt);
                if (!distance.containsKey(next)) {
                    distance.put(next, distance.get(crt) + 1);
                    q.offer(next);
                }
            }
        }
    }

    List<String> expand(String crt, Set<String> dict) {
        List<String> expansion = new ArrayList<String>();

        for (int i = 0; i < crt.length(); i++) {
            for (char ch = 'a'; ch <= 'z'; ch++) {
                if (ch != crt.charAt(i)) {
                    String expanded = crt.substring(0, i) + ch
                            + crt.substring(i + 1);
                    if (dict.contains(expanded)) {
                        expansion.add(expanded);
                    }
                }
            }
        }

        return expansion;
    }
}
View Code

 

posted @ 2016-10-26 17:20  coldyan  阅读(435)  评论(0编辑  收藏  举报