• 算法题解之图论与搜索


    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
  • 相关阅读:
    CSS 设置table下tbody滚动条
    PHP图片上传程序(完整版)
    Nginx无法监听虚拟VIP的问题报:99: Cannot assign requested address
    当切换用户时出现-bash-4.1$
    mysql 启动报错Host name could not be resolved解决办法
    设置博客园标题样式
    五步解决windows系统慢的问题
    Centos7下yum安装软件报错解决办法
    windows下使用mysqlbinlog做数据恢复时出现mysqlbinlog: File 'D:MariaDB' not found (Errcode: 2)
    Microsoft SQL Server Management Studio连接后报“ viewInfo (Microsoft.SqlServer.Management.SqlStudio.Expl”
  • 原文地址:https://www.cnblogs.com/coldyan/p/6001148.html
Copyright © 2020-2023  润新知