LintCode图论&搜索题总结
2016-06-19 16:20
267 查看
LintCode上Graph & Search那一章有这些题目:
15. Permutations
给定一连串数字,要求它的所有可能的排列。
比如123的全排列为[123, 132, 213, 231, 312, 321]
这道题用DFS搜索,递归搜索树如下所示:
画出递归搜索树之后,代码自然就不难了。
每次加入list数组中的元素不能在之前出现过。
16. Permutations II
这道题和上一道题有点类似,但是输入的数组会有重复的数字,比如[122]的全排列是[122, 212, 221]
这就要求我们在处理重复的元素上花一些功夫了。所以我们需要先对数组进行排序,然后只能连续地选,这样就可以避免生成重复的solution。
需要加一个visit数组,表示哪些已经访问过了。如果当前下标已经访问过了,则不要把它加进了。如果有2个连续的元素相等,并且前一个元素没有访问过,那么后面一个元素也不要加进去。
String Permutation I
跟Permutations那道题基本一样,只不过这次处理的是字符串,不是数字数组了。
需要注意的是,如果是求长度:
A) 对于String str来说,调用的是str.length()
B) 对于ArrayList<Integer> list来说,调用的是list.size()
C) 对于char[] arr来说,调用的是arr.length
如果是求是否包含某个元素:
A) 对于String str来说,调用的是str.indexOf(char c)
B) 对于ArrayList<Integer> list来说,调用的是list.contains(int a)
10. String Permutation II
跟上道题类似,不过输入多了重复的字符,比如abb。处理方法和Permutation II那道题是一样的,加入visit数组判断.
17. Subsets
给定一个含不同整数的集合,返回其所有的子集。子集中的元素排列必须是非降序的,解集必须不包含重复的子集。
这个跟permutation相比,就不用等到list的容量达到指定值才加进去了。每次DFS都能加元素
18. Subsets II
跟上一道题类似,只不过输入的数中会有重复的元素。注意要跳过重复元素
以上四道题都是典型的DFS深度搜索递归。他们的模板可以背下来。可以借助画图来帮助理解。
33. N-Queens
N皇后问题,输入一个数字n,要求出n*n方格下所有符合N皇后规则的棋盘格局。N皇后不能出现在同一直线上(横、竖、对角线)。
这种要求出所有方案类型的题目,典型的是用深搜DFS,并且这道题可以套DFS的模板。
一层一层的向下扫描,需要用到一个pos数组,其中pos[i]表示第i行皇后的位置,然后从第0开始递归,每一行都一次遍历各列,判断如果在该位置放置皇后会不会有冲突,以此类推,当到最后一行的皇后放好后,一种解法就生成了,将其存入结果res中,然后再还会继续完成搜索所有的情况。
34. N-Queens II
跟上道题很类似,只不过只要求出N皇后的个数,而不是列举出总方案数。记得要声明一个static全局静态变量来记录个数。
136. Palindrome Partitioning
对字符串进行划分,求出所有使得它能成为回文串的划分组合。
Given s =
这种题也是经典的DFS深搜题,可以直接套用DFS模板。
135. Combination Sum
给出一组候选数字(C)和目标数字(T),找到C中所有的组合,使找出的数字和为T。C中的数字可以无限制重复被选取。
例如,给出候选数组[2,3,6,7]和目标数字7,所求的解为:
[7],
[2,2,3]
由于是从set集合里找元素来进行组合,集合的特点就是每个元素是唯一的。这也是一道可以直接套用DFS模板的题目:
153. Combination Sum II
跟上道题不同的是,这次是从一群有重复的数字的集合里找组合,同一个数字会在集合里出现多次。
但是每个数字只允许取一次。需要注意的是对重复数字的处理,出现重复数字就跳过那一次循环。
90. k Sum II
解题思路: 由于k Sum是求个数,所以考虑动态规划,直接DFS会超时。而k Sum II 是求所有具体的解,所以直接DFS.
思路跟 subsets 类似,可以想象成求一些特殊的subsets,加入result时,要符合subset的和等于target
本题与 Combination Sum II 也非常类似
137. Clone Graph
给一个邻接表表示的图,返回一个它的深拷贝。这道题的关键在于用BFS首先克隆所有的点,然后再克隆所有的边。克隆点的时候需要用到HashSet来保存每一个节点。
127. Topological Sorting
拓扑排序,把一个有向无环图转换成一维的拓扑排序。拓扑排序是对有向无环图的一种排序。表示了顶点按边的方向出现的先后顺序。如果有环,则无法表示两个顶点的先后顺序。一个简单的求拓扑排序的算法:首先要找到任意入度为0的一个顶点,删除它及所有相邻的边,再找入度为0的顶点,以此类推,直到删除所有顶点。顶点的删除顺序即为拓扑排序。这里要用到BFS。
1)首先计算每个点的入度。
2)把入度为0的节点加进队列。
3)用队列进行BFS操作,每次从队列中拿出节点的时候,更新它的后继结点的入度(后继结点入度减1)。
如果要用DFS的话,参考这个博客http://www.geeksforgeeks.org/topological-sorting/
120. Word Ladder
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.
根据一定的单词变化规则,看从起始单词能不能经过这个规则变到终止单词,并求出最少通过多少次能变到。规则就是每次只能变动一个字母,并且变动完后的单词要在给定的字典中。如果把整个变化的过程用图画出来,会发现这其实是一个graph,而求最少的次数,求相当于求最短路径的长度。有点像求二叉树的最小高度,使用的是BFS层次遍历,用到队列和hashset,用到hashset的原因是把每次走过的点标记为已走过,防止再重复走。
需要注意对边界情况做处理,比如字典为空,比如初始单词和终止单词是一个单词。还有就是要先把队列大小size的值给用一个变量保存一下,因为每次出队列都会使得队列的size发生变化,为了使得每个点都能遍历到,一定要用中间变量保存一下size
121. Word Ladder II
这道题跟上道题类似,不过要求的不只是最短路径了,而是要把最短路径的多种可能组合都给返回。如果要求出所有的组合,这种题目一般是DFS。如果是求最短路,则是BFS。所以这道题的解法是BFS+DFS。
BFS主要就做2件事情:
1)标记每个点(单词)的位置,越靠近起始单词,数字就越小,越靠近终止单词,数字就越大。比如起始单词的位置为0
2)对于每个单词,求出有哪些单词可以直接到达它,也就是求每个单词的父节点。
DFS就是从终止节点到起始节点进行DFS。一旦找到了起始节点,就可以把路径上经过的节点加入res里了。若还未找到节点,则继续把当前节点加入res里。
531. Six Degrees
就是直接用BFS找最短长度。但是把HashMap换成HashSet就过不了了,需要注意的是,对于图来说,它的BFS的层次遍历,不能像树那样求高度那样每次length++。树是有规律的,从上到下是填满的,但是图就不一样了,所以要求路径长度,一定要根据它的上一个节点的长度+1.
431. Find the Connected Component in the Undirected Graph
找出无向图中所有的连通块,这道题好像就是Coursera上普林斯顿的算法课的练习题,有两种方法可以做,一种是并查集,还有一种就是BFS。
176. Route Between Two Nodes in Graph
在一个有向图里,问2个点是否是连通的。典型的BFS题,用BFS遍历,如果中途遇到了另外一个点,这说明可到达,否则不可达。
15. Permutations
给定一连串数字,要求它的所有可能的排列。
比如123的全排列为[123, 132, 213, 231, 312, 321]
这道题用DFS搜索,递归搜索树如下所示:
画出递归搜索树之后,代码自然就不难了。
每次加入list数组中的元素不能在之前出现过。
public ArrayList<ArrayList<Integer>> permute(ArrayList<Integer> nums) { ArrayList<ArrayList<Integer> > res = new ArrayList(); if (nums == null || nums.size() == 0) { return res; } ArrayList<Integer> list = new ArrayList<Integer>(); helper(res, list, nums); return res; } private void helper(ArrayList<ArrayList<Integer>> res, ArrayList<Integer> list, ArrayList<Integer> nums) { if (list.size() == nums.size()) { res.add(new ArrayList<Integer>(list)); return; } for (int i = 0; i < nums.size(); i++) { if (list.contains(nums.get(i))) { continue; } list.add(nums.get(i)); helper(res, list, nums); list.remove(list.size() - 1); } }时间复杂度是O(n! * n)
16. Permutations II
这道题和上一道题有点类似,但是输入的数组会有重复的数字,比如[122]的全排列是[122, 212, 221]
这就要求我们在处理重复的元素上花一些功夫了。所以我们需要先对数组进行排序,然后只能连续地选,这样就可以避免生成重复的solution。
需要加一个visit数组,表示哪些已经访问过了。如果当前下标已经访问过了,则不要把它加进了。如果有2个连续的元素相等,并且前一个元素没有访问过,那么后面一个元素也不要加进去。
public ArrayList<ArrayList<Integer>> permuteUnique(ArrayList<Integer> nums) { ArrayList<ArrayList<Integer> > res = new ArrayList(); if (nums == null || nums.size() == 0) { return res; } Collections.sort(nums); boolean[] visit = new boolean[nums.size()]; ArrayList<Integer> list = new ArrayList<Integer>(); helper(res, list, nums, visit); return res; } private void helper(ArrayList<ArrayList<Integer>> res, ArrayList<Integer> list, ArrayList<Integer> nums, boolean[] visit) { if (list.size() == nums.size()) { res.add(new ArrayList<Integer>(list)); return; } for (int i = 0; i < nums.size(); i++) { if (visit[i] || (i != 0 && nums.get(i) == nums.get(i-1) && visit[i-1])) { continue; } visit[i] = true; list.add(nums.get(i)); helper(res, list, nums, visit); list.remove(list.size() - 1); visit[i] = false; } }
String Permutation I
跟Permutations那道题基本一样,只不过这次处理的是字符串,不是数字数组了。
需要注意的是,如果是求长度:
A) 对于String str来说,调用的是str.length()
B) 对于ArrayList<Integer> list来说,调用的是list.size()
C) 对于char[] arr来说,调用的是arr.length
如果是求是否包含某个元素:
A) 对于String str来说,调用的是str.indexOf(char c)
B) 对于ArrayList<Integer> list来说,调用的是list.contains(int a)
private void dfs(List<String> res, char[] s, String path) { if (path.length() == s.length) { res.add(new String(path)); return; } for (int i = 0; i < s.length; i++) { if (path.indexOf(s[i]) != -1) { continue; } path += s[i]; dfs(res, s, path); path = path.substring(0, path.length() - 1); } } public List<String> stringPermutation2(String str) { List<String> res = new ArrayList<String>(); char[] s = str.toCharArray(); Arrays.sort(s); dfs(res, s, ""); return res; }
10. String Permutation II
跟上道题类似,不过输入多了重复的字符,比如abb。处理方法和Permutation II那道题是一样的,加入visit数组判断.
private void dfs(List<String> res, char[] s, String path, boolean[] visit) { if (path.length() == s.length) { res.add(new String(path)); return; } for (int i = 0; i < s.length; i++) { if (visit[i] == true || (i != 0 && s[i] == s[i-1] && !visit[i-1])) { continue; } visit[i] = true; path += s[i]; dfs(res, s, path, visit); path = path.substring(0, path.length() - 1); visit[i] = false; } } public List<String> stringPermutation2(String str) { List<String> res = new ArrayList<String>(); char[] s = str.toCharArray(); Arrays.sort(s); boolean[] visit = new boolean[str.length()]; dfs(res, s, "", visit); return res; }
17. Subsets
给定一个含不同整数的集合,返回其所有的子集。子集中的元素排列必须是非降序的,解集必须不包含重复的子集。
这个跟permutation相比,就不用等到list的容量达到指定值才加进去了。每次DFS都能加元素
public ArrayList<ArrayList<Integer>> subsets(int[] nums) { ArrayList<ArrayList<Integer>> res = new ArrayList(); ArrayList<Integer> list = new ArrayList<Integer>(); Arrays.sort(nums); helper(res, list, nums, 0); return res; } private void helper(ArrayList<ArrayList<Integer>> res, ArrayList<Integer> list, int[] nums, int pos) { res.add(new ArrayList<Integer>(list)); for (int i = pos; i < nums.length; i++) { list.add(nums[i]); helper(res, list, nums, i + 1); list.remove(list.size() - 1); } }
18. Subsets II
跟上一道题类似,只不过输入的数中会有重复的元素。注意要跳过重复元素
public ArrayList<ArrayList<Integer>> subsetsWithDup(ArrayList<Integer> S) { ArrayList<ArrayList<Integer>> res = new ArrayList(); ArrayList<Integer> list = new ArrayList<Integer>(); Collections.sort(S); helper(res, list, S, 0); return res; } private void helper(ArrayList<ArrayList<Integer>> res, ArrayList<Integer> list, ArrayList<Integer> num, int pos) { res.add(new ArrayList<Integer>(list)); for (int i = pos; i < num.size(); i++) { if (i != pos && num.get(i) == num.get(i-1)) { continue; } list.add(num.get(i)); helper(res, list, num, i + 1); list.remove(list.size() - 1); } }
以上四道题都是典型的DFS深度搜索递归。他们的模板可以背下来。可以借助画图来帮助理解。
33. N-Queens
N皇后问题,输入一个数字n,要求出n*n方格下所有符合N皇后规则的棋盘格局。N皇后不能出现在同一直线上(横、竖、对角线)。
这种要求出所有方案类型的题目,典型的是用深搜DFS,并且这道题可以套DFS的模板。
一层一层的向下扫描,需要用到一个pos数组,其中pos[i]表示第i行皇后的位置,然后从第0开始递归,每一行都一次遍历各列,判断如果在该位置放置皇后会不会有冲突,以此类推,当到最后一行的皇后放好后,一种解法就生成了,将其存入结果res中,然后再还会继续完成搜索所有的情况。
private ArrayList<String> draw(ArrayList<Integer> pos) { ArrayList<String> res = new ArrayList(); int n = pos.size(); for (int i = 0; i < n; i++) { String tmp = new String(); for (int j = 0; j < n; j++) { if (j == pos.get(i)) { tmp += "Q"; continue; } tmp += "."; } res.add(tmp); } return res; } private boolean isValid(ArrayList<Integer> pos, int col) { int row = pos.size(); for (int i = 0; i < row; i++) { // same column if (col == pos.get(i)) { return false; } // diagonal if (Math.abs(i - row) == Math.abs(pos.get(i) - col)) { return false; } } return true; } private void dfs(ArrayList<Integer> pos, ArrayList<ArrayList<String>> res, int n) { if (pos.size() == n) { ArrayList<String> tmp = draw(pos); res.add(tmp); return; } for (int i = 0; i < n; i++) { if (isValid(pos, i)) { pos.add(i); dfs(pos, res, n); pos.remove(pos.size() - 1); } } } ArrayList<ArrayList<String>> solveNQueens(int n) { ArrayList<ArrayList<String>> res = new ArrayList(); if (n <= 0) { return res; } dfs(new ArrayList<Integer>(), res, n); return res; }
34. N-Queens II
跟上道题很类似,只不过只要求出N皇后的个数,而不是列举出总方案数。记得要声明一个static全局静态变量来记录个数。
private static int res; private boolean isValid(ArrayList<Integer> pos, int col) { int row = pos.size(); for (int i = 0; i < row; i++) { // same column if (col == pos.get(i)) { return false; } // diagonal if (Math.abs(i - row) == Math.abs(pos.get(i) - col)) { return false; } } return true; } private void dfs(ArrayList<Integer> pos, int n) { if (pos.size() == n) { res++; return; } for (int i = 0; i < n; i++) { if (isValid(pos, i)) { pos.add(i); dfs(pos, n); pos.remove(pos.size() - 1); } } } public int totalNQueens(int n) { // res = 0; dfs(new ArrayList<Integer>(), n); return res; }
136. Palindrome Partitioning
对字符串进行划分,求出所有使得它能成为回文串的划分组合。
Given s =
"aab", return:
[ ["aa","b"], ["a","a","b"] ]
这种题也是经典的DFS深搜题,可以直接套用DFS模板。
private boolean isPalindrome(String s) { int left = 0, right = s.length() - 1; while (left < right) { if (s.charAt(left) != s.charAt(right)) { return false; } left++; right--; } return true; } private void dfs(List<List<String>>res, ArrayList<String> path, String s, int pos) { if (pos == s.length()) { res.add(new ArrayList<String>(path)); return; } for (int i = pos; i < s.length(); i++) { String prefix = s.substring(pos, i + 1); if (isPalindrome(prefix)) { path.add(prefix); dfs(res, path, s, i + 1); path.remove(path.size() - 1); } } } public List<List<String>> partition(String s) { List<List<String>> res = new ArrayList(); if (s == null) { return res; } dfs(res, new ArrayList<String>(), s, 0); return res; }
135. Combination Sum
给出一组候选数字(C)和目标数字(T),找到C中所有的组合,使找出的数字和为T。C中的数字可以无限制重复被选取。
例如,给出候选数组[2,3,6,7]和目标数字7,所求的解为:
[7],
[2,2,3]
由于是从set集合里找元素来进行组合,集合的特点就是每个元素是唯一的。这也是一道可以直接套用DFS模板的题目:
private void dfs(List<List<Integer>> res, ArrayList<Integer> path, int target, int[] candidates, int pos) { if (target == 0) { res.add(new ArrayList<Integer>(path)); return; } for (int i = pos; i < candidates.length; i++) { if (candidates[i] > target) { break; } path.add(candidates[i]); dfs(res, path, target - candidates[i], candidates, i); path.remove(path.size() - 1); } } public List<List<Integer>> combinationSum(int[] candidates, int target) { List<List<Integer>> res = new ArrayList(); if (candidates == null) { return res; } Arrays.sort(candidates); dfs(res, new ArrayList<Integer>(), target, candidates, 0); return res; }
153. Combination Sum II
跟上道题不同的是,这次是从一群有重复的数字的集合里找组合,同一个数字会在集合里出现多次。
但是每个数字只允许取一次。需要注意的是对重复数字的处理,出现重复数字就跳过那一次循环。
private void dfs(List<List<Integer>> res, ArrayList<Integer> path, int target, int[] num, int pos) { if (target == 0) { res.add(new ArrayList<Integer>(path)); return; } for (int i = pos; i < num.length; i++) { if (num[i] > target) { break; } if (i != pos && num[i] == num[i-1]) { continue; } path.add(num[i]); dfs(res, path, target - num[i], num, i + 1); path.remove(path.size() - 1); } } public List<List<Integer>> combinationSum2(int[] num, int target) { List<List<Integer>> res = new ArrayList(); if (num == null) { return res; } Arrays.sort(num); dfs(res, new ArrayList<Integer>(), target, num, 0); return res; }
90. k Sum II
解题思路: 由于k Sum是求个数,所以考虑动态规划,直接DFS会超时。而k Sum II 是求所有具体的解,所以直接DFS.
思路跟 subsets 类似,可以想象成求一些特殊的subsets,加入result时,要符合subset的和等于target
本题与 Combination Sum II 也非常类似
private void dfs(ArrayList<ArrayList<Integer>> res, ArrayList<Integer> path, int[] A, int k, int target, int pos) { if (path.size() == k) { if (target == 0) { res.add(new ArrayList<Integer>(path)); } return; } for (int i = pos; i < A.length; i++) { path.add(A[i]); dfs(res, path, A, k, target - A[i], i + 1); path.remove(path.size() - 1); } } public ArrayList<ArrayList<Integer>> kSumII(int[] A, int k, int target) { ArrayList<ArrayList<Integer>> res = new ArrayList(); if (k <= 0) { return res; } dfs(res, new ArrayList<Integer>(), A, k, target, 0); return res; }
137. Clone Graph
给一个邻接表表示的图,返回一个它的深拷贝。这道题的关键在于用BFS首先克隆所有的点,然后再克隆所有的边。克隆点的时候需要用到HashSet来保存每一个节点。
/** * 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) { if (node == null) { return null; } // 1. use bfs to get all nodes HashSet<UndirectedGraphNode> set = new HashSet<UndirectedGraphNode>(); Queue<UndirectedGraphNode> q = new LinkedList<UndirectedGraphNode>(); set.add(node); q.offer(node); while (!q.isEmpty()) { UndirectedGraphNode root = q.poll(); for (UndirectedGraphNode n : root.neighbors) { if (!set.contains(n)) { set.add(n); q.offer(n); } } } ArrayList<UndirectedGraphNode> nodes = new ArrayList<UndirectedGraphNode>(set); // 2. clone all nodes HashMap<UndirectedGraphNode, UndirectedGraphNode> map = new HashMap<UndirectedGraphNode, UndirectedGraphNode>(); for (UndirectedGraphNode tmp : nodes) { map.put(tmp, new UndirectedGraphNode(tmp.label)); } // 3. clone all edges for (UndirectedGraphNode tmp : nodes) { UndirectedGraphNode newNode = map.get(tmp); for (UndirectedGraphNode n : tmp.neighbors) { newNode.neighbors.add(map.get(n)); } } return map.get(node); } }
127. Topological Sorting
拓扑排序,把一个有向无环图转换成一维的拓扑排序。拓扑排序是对有向无环图的一种排序。表示了顶点按边的方向出现的先后顺序。如果有环,则无法表示两个顶点的先后顺序。一个简单的求拓扑排序的算法:首先要找到任意入度为0的一个顶点,删除它及所有相邻的边,再找入度为0的顶点,以此类推,直到删除所有顶点。顶点的删除顺序即为拓扑排序。这里要用到BFS。
1)首先计算每个点的入度。
2)把入度为0的节点加进队列。
3)用队列进行BFS操作,每次从队列中拿出节点的时候,更新它的后继结点的入度(后继结点入度减1)。
/** * 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) { // calculate in-degree of all nodes HashMap<DirectedGraphNode, Integer> map = new HashMap(); for (DirectedGraphNode node : graph) { for (DirectedGraphNode neighbor : node.neighbors) { if (map.containsKey(neighbor)) { map.put(neighbor, map.get(neighbor) + 1); } else { map.put(neighbor, 1); } } } // add nodes with 0 in-degrees into the queue and result array ArrayList<DirectedGraphNode> res = new ArrayList<DirectedGraphNode>(); Queue<DirectedGraphNode> q = new LinkedList<DirectedGraphNode>(); for (DirectedGraphNode node : graph) { if (!map.containsKey(node)) { q.offer(node); res.add(node); } } // BFS while (!q.isEmpty()) { DirectedGraphNode node = q.poll(); for (DirectedGraphNode neighbor : node.neighbors) { map.put(neighbor, map.get(neighbor) - 1); if (map.get(neighbor) == 0) { q.offer(neighbor); res.add(neighbor); } } } return res; } }
如果要用DFS的话,参考这个博客http://www.geeksforgeeks.org/topological-sorting/
120. Word Ladder
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.
根据一定的单词变化规则,看从起始单词能不能经过这个规则变到终止单词,并求出最少通过多少次能变到。规则就是每次只能变动一个字母,并且变动完后的单词要在给定的字典中。如果把整个变化的过程用图画出来,会发现这其实是一个graph,而求最少的次数,求相当于求最短路径的长度。有点像求二叉树的最小高度,使用的是BFS层次遍历,用到队列和hashset,用到hashset的原因是把每次走过的点标记为已走过,防止再重复走。
需要注意对边界情况做处理,比如字典为空,比如初始单词和终止单词是一个单词。还有就是要先把队列大小size的值给用一个变量保存一下,因为每次出队列都会使得队列的size发生变化,为了使得每个点都能遍历到,一定要用中间变量保存一下size
private String replace(String s, int index, char c) { char[] arr = s.toCharArray(); arr[index] = c; return new String(arr); } private ArrayList<String> findNeighbor(String word, Set<String> dict) { ArrayList<String> res = new ArrayList<String>(); for (int i = 0; i < word.length(); i++) { for (char c = 'a'; c <= 'z'; c++) { if (word.charAt(i) != c) { String tmp = replace(word, i, c); if (dict.contains(tmp)) { res.add(tmp); } } } } return res; } public int ladderLength(String start, String end, Set<String> dict) { if (dict == null) { return 0; } if (start.equals(end)) { return 1; } dict.add(start); dict.add(end); Queue<String> q = new LinkedList<String>(); HashSet<String> hash = new HashSet<String>(); q.offer(start); hash.add(start); int length = 1; while (!q.isEmpty()) { int size = q.size(); for (int i = 0; i < size; i++) { String word = q.poll(); for (String str : findNeighbor(word, dict)) { if (hash.contains(str)) { continue; } if (str.equals(end)) { return length + 1; } hash.add(str); q.offer(str); } } length++; } return 0; }
121. Word Ladder II
这道题跟上道题类似,不过要求的不只是最短路径了,而是要把最短路径的多种可能组合都给返回。如果要求出所有的组合,这种题目一般是DFS。如果是求最短路,则是BFS。所以这道题的解法是BFS+DFS。
BFS主要就做2件事情:
1)标记每个点(单词)的位置,越靠近起始单词,数字就越小,越靠近终止单词,数字就越大。比如起始单词的位置为0
2)对于每个单词,求出有哪些单词可以直接到达它,也就是求每个单词的父节点。
DFS就是从终止节点到起始节点进行DFS。一旦找到了起始节点,就可以把路径上经过的节点加入res里了。若还未找到节点,则继续把当前节点加入res里。
public List<List<String>> findLadders(String start, String end, Set<String> dict) { List<List<String>> res = new ArrayList(); HashMap<String, List<String>> map = new HashMap<String, List<String>>(); HashMap<String, Integer> distance = new HashMap<String, Integer>(); dict.add(start); dict.add(end); bfs(start, end, dict, map, distance); dfs(res, new ArrayList<String>(), start, end, map, distance); return res; } private ArrayList<String> findNeighbor(String word, Set<String> dict) { ArrayList<String> res = new ArrayList<String>(); for (int i = 0; i < word.length(); i++) { for (char c = 'a'; c <= 'z'; c++) { if (word.charAt(i) != c) { String tmp = word.substring(0, i) + c + word.substring(i+1); if (dict.contains(tmp)) { res.add(tmp); } } } } return res; } private void bfs(String start, String end, Set<String> dict, HashMap<String, List<String>> map, HashMap<String, Integer> distance) { Queue<String> q = new LinkedList<String>(); q.offer(start); distance.put(start, 0); while (!q.isEmpty()) { String father = q.poll(); for (String str : findNeighbor(father, dict)) { if (map.get(str) == null) { ArrayList<String> tmp = new ArrayList<String>(); tmp.add(father); map.put(str, tmp); } else { map.get(str).add(father); } // distance同时起到了判断有没有访问过的作用 if (!distance.containsKey(str)) { q.offer(str); distance.put(str, distance.get(father) + 1); } } } } private void dfs(List<List<String>> res, List<String> path, String start, String end, HashMap<String, List<String>> map, HashMap<String, Integer> distance) { if (end.equals(start)) { path.add(start); Collections.reverse(path); res.add(new ArrayList<String>(path)); Collections.reverse(path); path.remove(path.size() - 1); return; } for (String next : map.get(end)) { if (distance.get(next) + 1 == distance.get(end)) { path.add(end); dfs(res, path, start, next, map, distance); path.remove(path.size() - 1); } } }
531. Six Degrees
就是直接用BFS找最短长度。但是把HashMap换成HashSet就过不了了,需要注意的是,对于图来说,它的BFS的层次遍历,不能像树那样求高度那样每次length++。树是有规律的,从上到下是填满的,但是图就不一样了,所以要求路径长度,一定要根据它的上一个节点的长度+1.
/** * 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) { if (s == t) { return 0; } Queue<UndirectedGraphNode> q = new LinkedList<UndirectedGraphNode>(); HashMap<UndirectedGraphNode, Integer> hash = new HashMap<UndirectedGraphNode, Integer>(); q.offer(s); hash.put(s, 0); int distance = 0; while(!q.isEmpty()) { UndirectedGraphNode head = q.poll(); int size = head.neighbors.size(); for (int i = 0; i < size; i++) { UndirectedGraphNode node = head.neighbors.get(i); if (hash.containsKey(node)) { continue; } if (node == t) { return hash.get(head) + 1; } q.offer(node); hash.put(node, hash.get(head) + 1); } } return -1; } }
431. Find the Connected Component in the Undirected Graph
找出无向图中所有的连通块,这道题好像就是Coursera上普林斯顿的算法课的练习题,有两种方法可以做,一种是并查集,还有一种就是BFS。
/** * 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 void bfs(List<List<Integer>> res, Set<UndirectedGraphNode> set, UndirectedGraphNode node) { Queue<UndirectedGraphNode> queue = new LinkedList<UndirectedGraphNode>(); List<Integer> list = new ArrayList<Integer>(); list.add(node.label); queue.offer(node); set.add(node); while (!queue.isEmpty()) { UndirectedGraphNode head = queue.poll(); List<UndirectedGraphNode> next = head.neighbors; for (UndirectedGraphNode nextNode: next) { if (!set.contains(nextNode)) { list.add(nextNode.label); set.add(nextNode); queue.offer(nextNode); } } } Collections.sort(list); res.add(list); } public List<List<Integer>> connectedSet(ArrayList<UndirectedGraphNode> nodes) { List<List<Integer>> res = new ArrayList<List<Integer>>(); Set<UndirectedGraphNode> set = new HashSet<UndirectedGraphNode>(); for (UndirectedGraphNode node: nodes) { if (!set.contains(node)) { bfs(res, set, node); } } return res; } }
176. Route Between Two Nodes in Graph
在一个有向图里,问2个点是否是连通的。典型的BFS题,用BFS遍历,如果中途遇到了另外一个点,这说明可到达,否则不可达。
public boolean hasRoute(ArrayList<DirectedGraphNode> graph, DirectedGraphNode s, DirectedGraphNode t) { HashSet<DirectedGraphNode> visit = new HashSet(); Queue<DirectedGraphNode> q = new LinkedList(); if (t == s) { return true; } q.offer(s); while (!q.isEmpty()) { DirectedGraphNode head = q.poll(); ArrayList<DirectedGraphNode> arr = head.neighbors; for (DirectedGraphNode next : arr) { if (!visit.contains(next)) { if (next == t) { return true; } visit.add(next); q.offer(next); } else { continue; } } } return false; }
相关文章推荐
- Search Engine XSS Worm
- 简介JavaScript中search()方法的使用
- JavaScript禁止复制与粘贴的实现代码
- js闭包实现按秒计数
- Win2003利用dfs(分布式文件系统)在负载均衡下的文件同步配置方案
- 向大家推荐一个收集整理正则表达式的网站
- Js&Vbs正则表达式替换重复的字符
- 限制文本框中只能输入实数或整数,其它字符无效,有劳大家了!
- 关于IE的RegExp.exec的问题
- 最严谨的校验email地址的正则表达式及各种语言对应版
- Google Map Api和GOOGLE Search Api整合实现代码
- javascript中的window.location.search方法简介
- 使用javaScript动态加载Js文件和Css文件
- 教你使用javascript简单写一个页面模板引擎
- Microsoft Search 服务无法启动 解决办法.
- JavaScript操作 url 中 search 部分方法函数
- 正则表达式中特殊符号及正则表达式的几种方法总结(replace,test,search)
- PHP 在数组中搜索给定的简单实例 array_search 函数
- js通过location.search来获取页面传来的参数
- win2003分布式文件系统(dfs)配置方法[图文详解]