本文我们就Leetcode中的一个类型的题目backtracking进行一系列的总结和归纳。 backtracking这个方法本质是建立在递归的基础上,不断尝试新的路径,这里关键是每次尝试完以后需要退回来也就是回溯。
如果你已经找到了解决方案,那么返回成功
for(现在位置可以的所有可能选择){
选择其中一个方案然后沿着路径前进一步
使用递归的方法从新的位置解决问题
如果新的位置可以成功解决,向上一级返回成功
从现在位置恢复到循环之前的位置 }
如果到这里表示仍然没有成功,返回失败
下面我们来看一下的例题:
package DFS;import java.util.ArrayList;import java.util.Arrays;import java.util.HashMap;import java.util.LinkedList;import java.util.List;import java.util.Set;import java.util.Stack;public class DFSProblem { public static void main(String[] args) { List
> res=new ArrayList
>(); res=getFactors(12); System.out.println(res.size()); } /* * 113. Path Sum II * 11.18 By Mingyang * 典型的backtracking,不过注意,这里的值可能是负数,所以不能用sum小于0来做任何判断 * 1.长度标准:无 * 2.可选的范围:每一个node的左边和右边子节点(不为空的情况)。 * 3.往前走一步:temp加一个,sum减一个 * 4.后退一步:把temp remove掉 * 5.特殊的case:root==null也就是走到了根节点的下一个空了 */ public List
> pathSum(TreeNode root, int sum) { List
> res = new ArrayList
>(); List temp = new ArrayList (); dfs(root, sum, res, temp); return res; } public void dfs(TreeNode root, int sum, List
> res, List temp){ if(root==null) return; temp.add(root.val); if(root.left==null && root.right==null ){ if(root.val==sum) res.add(new ArrayList (temp)); return; } if(root.left!=null) { dfs(root.left,sum-root.val,res,temp); temp.remove(temp.size()-1); } if(root.right!=null) { dfs(root.right,sum-root.val,res,temp); temp.remove(temp.size()-1); } } /* * 320 Generalized Abbreviation * 2016-3-14 by Mingyang * Given "word" Return the following list (order does not matter): * ["word", "1ord", "w1rd", "wo1d", "wor1", "2rd", "w2d", "wo2", "1o1d", "1or1", "w1r1", "1o2", "2r1", "3d", "w3", "4"] * 这道题目的题意是比较难懂的,网上找了很多,都是copy过来copy过去的,都是一个答案,所以后面自己想出来了一个最好的题意的解释。就是把四个数xxxx,用分别用一个1,两个1,三个1来代替其中的字母 * 如果出现111x就自动转化为3x,所以我的思路就是先找出所有包含1的排列组合,然后转换为标准格式。典型的backtracking。 * 1.长度标准:有1,2,3(0和4)单独来看的 * 2.可选的范围:从start开始的每一个字符(有一定的顺序,所以用start) * 3.往前走一步:count-1,start+1,改变string * 4.后退一步:把string改回来 * 5.特殊的case:count等于0,也就是输入了相应的个数的1 */ public static List generateAbbreviations(String word) { List res = new ArrayList (); for(int i=1;i res,int count,int start,String copy){ if(count==0){ copy=format(copy); res.add(copy); return; } StringBuffer sb=new StringBuffer(); if(copy.length()==0){ sb.append(word); }else{ sb.append(copy); } for(int i=start;i 放map中 map.put(str.substring(j, k+1), i);//把string的 放map中 } if(dfs(pattern, i+1, str, k+1, map)){ //dfs return true; } if(val == null){ // backtracking map.remove(pattern.charAt(i)); map.remove(str.substring(j, k+1)); } } } return false;//注意不要忘了,不能继续走下去拿到合适的match一定要return false } /* * 267. Palindrome Permutation II * 2016-3-13 by Mingyang * Given a string s, return all the palindromic permutations (without duplicates) of it. Return an empty list if no palindromic permutation could be form. * Given s = "aabb", return ["abba", "baab"]. * Given s = "abc", return []. * 这道题目就相对来说简单多了,就跟permutation II类似,如果不写那个比较重要的判断句,那么久会出现很多的重复的。整题原理就是找到所有的组合可能性,再选择其中合适的 * 1.长度标准:无(固定) * 2.可选的范围:对于一个array的所有permutation,这个array有重复(所以多了一句判断句),而且前后可以颠倒(所以用visited)。具体参考permutation II * 3.往前走一步:sb加入这个数,visited改为true * 4.后退一步:sb减去,visited改为false * 5.特别的case:到了长度检查。 * 6.关于重复:因为可以有重复aabb可能会有abba和abba,第一个a在第一个,第二个a在第二个 */ public static List generatePalindromes(String s) { List res=new ArrayList (); boolean[] visited=new boolean[s.length()]; char[] model=s.toCharArray(); StringBuffer sb=new StringBuffer(); dfs(res,model,visited,sb); return res; } public static void dfs(List res,char[] model,boolean[] visited,StringBuffer sb){ if(sb.toString().length()==model.length){ if(isPalindrome(sb.toString())){ res.add(sb.toString()); } return; } for(int i=0;i 0 && !visited[i - 1] && model[i] == model[i - 1]) // 这个非常的重要!!!!!!!!!! continue; if(!visited[i]){ sb.append(model[i]); visited[i]=true; dfs(res,model,visited,sb); visited[i]=false; sb.deleteCharAt(sb.length()-1); } } } /* * 254 Factor Combinations * 2016-3-13 by Mingyang * Numbers can be regarded as product of its factors. For example, * 8 = 2 x 2 x 2; 8= 2 x 4. * Write a function that takes an integer n and return all possible combinations of its factors * 1.长度标准:无(固定) * 2.可选的范围:最开始是从2到n-1的所有整数,每一次递进以后。就是从上次取得那个数起到n-1的所有整数。 * 3.往前走一步:temp加入这个数,num乘以这个数--网上的版是这个数除以i。少了一个参数,一样的原理 * 4.后退一步:temp remove * 5.特别的case:到了长度检查。 */ public static List
> getFactors(int n) { List
> res=new ArrayList
>(); List temp=new ArrayList (); dfs1(res,temp,1,n,2); return res; } public static void dfs1(List
> res, List temp, int num,int n,int start){ if(num==n){ res.add(new ArrayList (temp)); return; } if(num>n) return; for(int i=start;i > combinationSum(int[] candidates, int target) { List
> res = new ArrayList
>(); List temp = new ArrayList (); if (candidates == null || candidates.length == 0) return res; Arrays.sort(candidates); dfs(candidates, target, 0, temp, res); return res; } public void dfs(int[] candidates, int remain, int start,List temp, List
> res) { if (remain == 0) { res.add(new ArrayList (temp)); return; } if (remain < 0) return; for (int i = start; i < candidates.length; i++) { if (i > 0 && candidates[i] == candidates[i - 1]) // 这里就是跳过那个重复的元素,因为每次已经可以重复使用自己了,这一步很重要!! continue; temp.add(candidates[i]); dfs(candidates, remain - candidates[i], i, temp, res); temp.remove(temp.size() - 1); } } /* * 40. Combination Sum II * 2015-12-13 by Mingyang * Each number in C may only be used * once in the combination. * 在这里我们还是需要在每一次for循环前做一次判断,因为虽然一个元素不可以重复使用,但是如果这个元素重复出现是允许的, * 但是为了避免出现重复的结果集,我们只对于第一次得到这个数进行递归,接下来就跳过这个元素了, * 因为接下来的情况会在上一层的递归函数被考虑到,这样就可以避免重复元素的出现。 Each number in C may only be used * once in the combination. I 是The same repeated number may be chosen from C unlimited number of times. * 1.长度标准:无(固定) * 2.可选的范围:从start开始到最后一个数 * 3.往前走一步:temp加入这个数,remain再减去 * 4.后退一步:temp减去这个数 * 5.特别的case:剩下的小于0直接return * 6.关于重复:11 和1 在第一个1用了以后,第二个1就不用了 */ public List
> combinationSum2(int[] candidates, int target) { List
> res = new ArrayList
>(); List temp = new ArrayList (); if (candidates == null || candidates.length == 0) return res; Arrays.sort(candidates); dfs2(candidates, target, 0, temp, res); return res; } public void dfs2(int[] candidates, int remain, int begin,List temp, List
> res) { if (remain == 0) { res.add(new ArrayList (temp)); return; } if (remain < 0) return; for (int i = begin; i < candidates.length; i++) { if (i > begin && candidates[i] == candidates[i - 1])//去重复 continue; temp.add(candidates[i]); dfs2(candidates, remain - candidates[i], i + 1, temp, res);//所以在1中,这里的下一个还是i temp.remove(temp.size() - 1); } } /* * 216. Combination Sum III * 2015-12-18 by Mingyang * i一定要取到9,虽然大小聪明,想只取到7,但是后面的遍历可能也会遍历到9啊。 * 1.长度标准:无(固定等于k) * 2.可选的范围:从start开始到9 * 3.往前走一步:temp加入这个数,k-1表示又来了一位,n-i,然后start加1表示从下一位加起 * 4.后退一步:temp减去这个数 * 5.特别的case:剩下的小于0直接return * 6.关于重复:11 和1 在第一个1用了以后,第二个1就不用了 */ public List
> combinationSum3(int k, int n) { List
> res = new ArrayList
>(); List temp = new ArrayList (); dfs(res, temp, k, n, 1); return res; } public void dfs(List
> res, List temp, int k, int n,int start) { if (k == 0) { if (n == 0) { res.add(new ArrayList (temp)); } return; } for (int i = start; i <= 9; i++) { temp.add(i); dfs(res, temp, k - 1, n - i, start+1); temp.remove(temp.size() - 1); } } /* * 131. Palindrome Partitioning * 2015-12-17 by Mingyang * Return all possible palindrome partitioning of s. * 1.长度标准:无(固定) * 2.可选的范围:从start开始到最后一个 * 3.往前走一步:temp加入这个数,然后start加1表示从下一位加起 * 4.后退一步:temp减去这个数 * 5.特别的case:start到最后一位 * 6.关于重复:无 */ public List
> partition(String s) { List item = new ArrayList (); List
> res = new ArrayList
>(); if (s == null || s.length() == 0) return res; dfs(s, 0, item, res); return res; } public void dfs(String s, int start, List item,List
> res) { if (start == s.length()) { res.add(new ArrayList (item)); return; } for (int i = start; i < s.length(); i++) { String str = s.substring(start, i + 1);// 每一轮dfs进来都是先取第一个数,start index,可以不用stringbuffer来存 if (isPalindrome(str)) { item.add(str); dfs(s, i + 1, item, res);// 上面取到i,所以下一个start index就是i+1 item.remove(item.size() - 1); } } } public static boolean isPalindrome(String s) { int low = 0; int high = s.length() - 1; while (low < high) { if (s.charAt(low) != s.charAt(high)) return false; low++; high--; } return true; } /* * 93. Restore IP Addresses * 2015.12.16 by Mingyang * 这里不用StringBuffer因为我们最后要检查每一小块string到底是否符合要求 * 1.长度标准:无(固定) * 2.可选的范围:从start开始到最后一个 * 3.往前走一步:stringbuffer 变成string跟前面的相加成为一个新的string,然后start加1表示从下一位加起,然后count也减一个 * 4.后退一步:不用,因为传进去是string,不会对当前状态进行影响 * 5.特别的case:count小于等于0 * 6.关于重复:无 */ public static List restoreIpAddresses1(String s) { List res = new ArrayList (); dfs(res, s, 0, "", 4); return res; } public static void dfs(List res, String s, int start, String item,int count) { if (start == s.length() && count == 0) { res.add(item); return; } if (count <= 0) { return; } StringBuffer sb = new StringBuffer(); for (int i = start; i < s.length(); i++) { sb.append(s.charAt(i)); if (isValid(sb.toString())) { dfs(res, s, i + 1, item.isEmpty() ? (sb.toString()): (item + '.' + sb.toString()), count-1); } else { return;// 为什么需要写这个呢,因为这个可以防止多余的计算,比如数字太大2552551113,会让下面的isValid失效,就是省去多余的 } } } public static boolean isValid(String s) { if (s.charAt(0) == '0') return s.equals("0"); // 如果以0开头的,必须要检查是否等于001,011等不合格的,若开头为0,整个数必须为0 System.out.println(s); int num = Integer.parseInt(s); if (num <= 255 && num > 0) return true; else return false; } /* * 78. Subsets * 2015.12.16 by Mingyang * 注意这里虽然感觉很像combine,但是这个array可以随意的,可以为任意数的array,所以还是得用array来 * 并且那个array要sort一下,记住,千万不要忘了加空集 * 1.长度标准:从空集,到本身长度的集合 * 2.可选的范围:从start开始到最后一个 * 3.往前走一步:item加一个,然后start加1表示从下一位加起,然后count也减一个 * 4.后退一步:不用,因为传进去是string,不会对当前状态进行影响 * 5.特别的case:count小于等于0 * 6.关于重复:无 */ public static List
> subsets(int[] nums) { List
> res = new ArrayList
>(); ArrayList item = new ArrayList (); if (nums.length == 0 || nums == null) return res; Arrays.sort(nums); for (int len = 1; len <= nums.length; len++) dfs3(nums, 0, len, item, res); res.add(new ArrayList ()); return res; } public static void dfs3(int[] S, int start, int len, List item,List
> res) { if (item.size() == len) { res.add(new ArrayList (item)); return; } for (int i = start; i < S.length; i++) { item.add(S[i]); dfs3(S, i + 1, len, item, res); item.remove(item.size() - 1); } } /* * 90. Subsets II 12.16 by Mingyang * 这里我们将后面重复的部分算进来,就是当有重复出现的时候,结果不能重复,我就加了一个boolean array * 1.长度标准:从空集,到本身长度的集合 * 2.可选的范围:从start开始到最后一个 * 3.往前走一步:item加一个,然后start加1表示从下一位加起,然后count也减一个 * 4.后退一步:不用,因为传进去是string,不会对当前状态进行影响 * 5.特别的case:count小于等于0 * 6.关于重复:这里有重复出现,所以多了boolean array */ public static List
> subsetsWithDup(int[] nums) { List
> res = new ArrayList
>(); ArrayList item = new ArrayList (); if (nums.length == 0 || nums == null) return res; Arrays.sort(nums); boolean[] array = new boolean[nums.length]; for (int len = 1; len <= nums.length; len++) dfs3(nums, 0, len, item, res, array); res.add(new ArrayList ()); return res; } public static void dfs3(int[] S, int start, int len, List item,List
> res, boolean[] array) { if (item.size() == len) { res.add(new ArrayList (item)); return; } for (int i = start; i < S.length; i++) { if (i != 0 && S[i] == S[i - 1] && array[i - 1] == false)//这句非常关键,表示出了第一个以外,所有的跟前面一样的,并且前面还没用过(多半用了被重新还原的) continue; item.add(S[i]); array[i] = true; dfs3(S, i + 1, len, item, res, array); item.remove(item.size() - 1); array[i] = false; } } /* * 79. Word Search * 2015.12.16 by Mingyang * 这里的起点就是遍历所有的点,找出这个起点 * 我自己做的时候,用了一个StringBuffer来进行加减,判断条件就是sb和word相等,但是!每次加减sb造成了时间上的浪费 * 这里我们用一个index来就好了,每次完了以后,index+1.这样的话我们就可以通过index和word的长度来判断 * 另外有一个技巧就是index不用自己加加,只用加1,这样不会改变index的本质,不用退回来再减一个了。 * 1.长度标准:所有表格进行遍历,对每一个进行dfs,只要有一个可以就成功了 * 2.可选的范围:没有可选范围,已经规定死了xy轴 * 3.往前走一步:可以往上下左右四个方向分别加1 * 4.后退一步:mark unvisited * 5.特别的case:出界了,访问过了,或者达标了 * 6.关于重复:无 */ public boolean exist(char[][] board, String word) { int m = board.length; int n = board[0].length; boolean[][] visited = new boolean[m][n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (dfs4(board, word, 0, i, j, visited)) return true; } } return false; } public boolean dfs4(char[][] board, String word, int index, int rowindex,int colindex, boolean[][] visited) { if (index == word.length()) return true; if (rowindex < 0 || colindex < 0 || rowindex >= board.length|| colindex >= board[0].length) return false; if (visited[rowindex][colindex]) return false; if (board[rowindex][colindex] != word.charAt(index)) return false; visited[rowindex][colindex] = true; boolean res = dfs4(board, word, index + 1, rowindex - 1, colindex,visited) || dfs4(board, word, index + 1, rowindex + 1, colindex, visited) || dfs4(board, word, index + 1, rowindex, colindex + 1, visited) || dfs4(board, word, index + 1, rowindex, colindex - 1, visited); visited[rowindex][colindex] = false; return res; } /* * 77. Combinations * 2015.12.16 by Mingyang * 这里有个一个start的多的参数以后就可以保证所有序列按顺序输出的,并且不会重复 12-13-14-23-24-34 * 1.长度标准:无 * 2.可选的范围:从start到最后一个 * 3.往前走一步:可以往上下左右四个方向分别加1 * 4.后退一步:mark unvisited * 5.特别的case:出界了,访问过了,或者达标了 * 6.关于重复:无 */ public static List
> combine(int n, int k) { List
> res = new ArrayList
>(); if (n <= 0 || n < k) return res; List item = new ArrayList (); dfs2(n, k, 1, item, res);// because it need to begin from 1-------从1开始的哦 return res; } private static void dfs2(int n, int k, int start, List item,List
> res) { if (item.size() == k) { res.add(new ArrayList (item));// because item is ArrayList so it will not disappear from stack to stack return; } for (int i = start; i <= n; i++) { // 这里多加了一个start,这样就不用再判断重复了 item.add(i); dfs2(n, k, i + 1, item, res); item.remove(item.size() - 1); } } /* * 51. N-Queens * 2016.3.12 by Mingyang * 在这道题目里面,到了最后一步,才把整个棋盘转换成List of String * 其余的大部分时间都是只是把棋盘相应的位置填满 另外这道题目走的方式,不是像word * search那么上下左右到处走,而是在列数确定的情况下,行数从第一个到最后一个loop * 1.长度标准:无 * 2.可选的范围:在col固定的情况下,遍历所有的row * 3.往前走一步:如果某一个row可以就检查是否validate,并且把棋盘上的值改了 * 4.后退一步:棋盘上的值改回来 * 5.特别的case:column走到了最后一个 * 6.关于重复:无 */ public List
> solveNQueens(int n) { char[][] board = new char[n][n]; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) board[i][j] = '.'; List
> res = new ArrayList
>(); dfs(board, 0, res); return res; } private void dfs(char[][] board, int colIndex, List
> res) { if (colIndex == board.length) { res.add(construct(board)); return; } for (int i = 0; i < board.length; i++) { if (validate(board, i, colIndex)) { board[i][colIndex] = 'Q'; dfs(board, colIndex + 1, res); board[i][colIndex] = '.'; } } } private boolean validate(char[][] board, int x, int y) { for (int i = 0; i < board.length; i++) { for (int j = 0; j < y; j++) { if (board[i][j] == 'Q'&& (x + j == y + i || x + y == i + j || x == i)) return false; } } return true; } private List construct(char[][] board) { List res = new LinkedList (); for (int i = 0; i < board.length; i++) { String s = new String(board[i]); res.add(s); } return res; } /* * 52. N-Queens II * 2015.12.16 by Mingyang * 1.长度标准:无 * 2.可选的范围:在row固定的情况下,遍历所有的列 * 3.往前走一步:如果某一个col可以就检查是否validate,并且存上column的信息 * 4.后退一步:棋盘上的值改回来 * 5.特别的case:row走完了 * 6.关于重复:无 */ public int totalNQueens(int n) { int[] res = { 0 }; if (n <= 0) return res[0]; int[] columnVal = new int[n]; DFS_helper(n, res, 0, columnVal); return res[0]; } public void DFS_helper(int nQueens, int[] res, int row, int[] columnVal) { if (row == nQueens) { res[0] += 1; } else { for (int i = 0; i < nQueens; i++) { columnVal[row] = i;// (row,columnVal[row)==>(row,i) if (isValid(row, columnVal)) DFS_helper(nQueens, res, row + 1, columnVal); } } } public boolean isValid(int row, int[] columnVal) { for (int i = 0; i < row; i++) { if (columnVal[row] == columnVal[i]|| Math.abs(columnVal[row] - columnVal[i]) == row - i) return false; } return true; } /* * 46. Permutations * 2015.12.13 by Mingyang 方法还是原来那个套路,还是用一个循环递归处理子问题。 * 区别是这里并不是一直往后推进的,前面的数有可能放到后面,所以我们需要维护一个visited数组来表示该元素是否已经在当前结果中, * 因为每次我们取一个元素放入结果,然后递归剩下的元素,所以不会出现重复 * 这道题还有一个扩展就是如果元素集合中会出现重复,那么意味着我们需要跳过一些重复元素 * 1.长度标准:无 * 2.可选的范围:任何一个都可以选!没有顺序 * 3.往前走一步:在未访问某一个的前提下,直接带入 * 4.后退一步:remove * 5.特别的case:size到了 * 6.关于重复:无 */ public List
> permute(int[] nums) { List
> res = new ArrayList
>(); if (nums == null || nums.length == 0) return res; boolean[] visited = new boolean[nums.length]; List temp = new ArrayList (); dfs3(nums, visited, temp, res); return res; } private void dfs3(int[] nums, boolean[] visited, List temp,List
> res) { if (temp.size() == nums.length) { res.add(new ArrayList (temp)); return; } for (int i = 0; i < nums.length; i++) { if (!visited[i]) { visited[i] = true; temp.add(nums[i]); dfs3(nums, visited, temp, res); temp.remove(temp.size() - 1); visited[i] = false; } } } /* * 47. Permutations II * 2015.12.13 by Mingyang 唯一的区别就是在这个题目中元素集合可以出现重复。 * 这给我们带来一个问题就是如果不对重复元素加以区别, 那么类似于{1,1,2}这样的例子我们会有重复结果出现。那么如何避免这种重复呢? * 方法就是对于重复的元素循环时跳过递归函数的调用,只对第一个未被使用的进行递归, * 我们那么这一次结果会出现在第一个的递归函数结果中,而后面重复的会被略过。 如果第一个重复元素前面的元素还没在当前结果中,那么我们不需要进行递归。 * 首先我们要对元素集合排序,从而让重复元素相邻,接下来就是一行代码对于重复元素和前面元素使用情况的判断即可。 * 这样的解法是带有一般性的,把这个代码放到Permutations中也是正确的,所以如果熟悉的话, * 面试时如果碰到这个题目建议直接实现这个代码,不要假设元素没有重复,当然可以跟面试官讨论,不过一般来说都是要考虑这个情况的哈。 * 1.长度标准:无 * 2.可选的范围:任何一个都可以选!没有顺序 * 3.往前走一步:在未访问某一个的前提下,直接带入 * 4.后退一步:remove * 5.特别的case:size到了 * 6.关于重复:这里对于重复有要求,因为这个可以允许重复的字符,所以如果我等于前面的一个,并且前面的一个未访问(其实已经访问并且清除了)直接return */ public List
> permuteUnique(int[] nums) { List
> res = new ArrayList
>(); if (nums == null || nums.length == 0) return res; Arrays.sort(nums); boolean[] visited = new boolean[nums.length]; List temp = new ArrayList (); dfs4(nums, visited, temp, res); return res; } private void dfs4(int[] nums, boolean[] visited, List temp,List
> res) { if (temp.size() == nums.length) { res.add(new ArrayList (temp)); return; } for (int i = 0; i < nums.length; i++) { if (i > 0 && !visited[i - 1] && nums[i] == nums[i - 1]) // 这个非常的重要!!!!!!!!!! continue; if (!visited[i]) { visited[i] = true; temp.add(nums[i]); dfs4(nums, visited, temp, res); temp.remove(temp.size() - 1); visited[i] = false; } } } /* * 37. Sudoku Solver * 2015.12.13 by Mingyang * 1.长度标准:无 * 2.可选的范围:所有木有值得点,选取从1到9的数字 * 3.往前走一步:如果放进去是validate的,那就放 * 4.后退一步:若果放进去,后面是false的,就把这个点改回来 * 5.特别的case:无了 * 6.关于重复:无 * 本题目的isValid是难点 */ public void solveSudoku(char[][] board) { if (board == null || board.length == 0) return; helper(board); } private boolean helper(char[][] board) { for (int i = 0; i < board.length; i++) { for (int j = 0; j < board[0].length; j++) { if (board[i][j] == '.') { for (char num = '1'; num <= '9'; num++) { // 尝试 if (isValid(board, i, j, num)) { board[i][j] = num; if (helper(board)) return true; else board[i][j] = '.';// 回退 } } return false; } } } return true; } private boolean isValid(char[][] board, int i, int j, char c) { // check column for (int row = 0; row < 9; row++) if (board[row][j] == c) return false; // check row for (int col = 0; col < 9; col++) if (board[i][col] == c) return false; // check block for (int row = i / 3 * 3; row < i / 3 * 3 + 3; row++) for (int col = j / 3 * 3; col < j / 3 * 3 + 3; col++) if (board[row][col] == c) return false; return true; } /* * 22. Generate Parentheses * 2015.12.13 by Mingyang * 1.长度标准:无 * 2.可选的范围:只有左括号和右括号,先一直加左边,完了再加右边 * 3.往前走一步:sb加了,left或right减一 * 4.后退一步:sb remove * 5.特别的case:left和right都为0 * 6.关于重复:无 */ public static List generateParenthesis(int n) { List res = new ArrayList (); if (n <= 0) return res; StringBuffer sb = new StringBuffer(); dfs100(n, n, res, sb); return res; } public static void dfs100(int left, int right, List res,StringBuffer sb) { if (left == 0 && right == 0) { res.add(sb.toString()); System.out.println(sb.toString()); return; } if (left > right) return; if (left < 0 || right < 0) return; sb.append('('); dfs100(left - 1, right, res, sb); sb.deleteCharAt(sb.length() - 1); sb.append(')'); dfs100(left, right - 1, res, sb); sb.deleteCharAt(sb.length() - 1); } /* * 17. Letter Combinations of a Phone Number * 2015.11.30 by Mingyang 是对keyboard的循环 * 1.长度标准:无 * 2.可选的范围:数字对应的keyboard的string遍历每一个char * 3.往前走一步:加上这个char以后,index又加1 * 4.后退一步:stringbuffer remove * 5.特别的case:长度相等 * 6.关于重复:无 */ public List letterCombinations(String digits) { List result = new ArrayList (); if (digits == null || digits.length() == 0) return result; String[] keyboard = { "", "", "abc", "def", "ghi", "jkl", "mno","pqrs", "tuv", "wxyz" }; StringBuilder current = new StringBuilder(); int index = 0; dfs(digits, index, current, keyboard, result); return result; } private void dfs(String digits, int index, StringBuilder current,String[] keyboard, List result) { if (index == digits.length()) { result.add(current.toString()); return; } int num = digits.charAt(index) - '0';// get integer number for (int i = 0; i < keyboard[num].length(); i++) { current.append(keyboard[num].charAt(i)); dfs(digits, index + 1, current, keyboard, result); current.deleteCharAt(current.length() - 1); } } /* * Number of Islands 12.19 by Mingyang * 直接设一个叫count的值,没遇到一个1,就把所有相连的1全部变为0,这样,到底遇到几次1,就是最终有几个小岛啦 */ public int numIslands(char[][] grid) { if (grid == null || grid.length == 0 || grid[0].length == 0) return 0; int count = 0; for (int i = 0; i < grid.length; i++) { for (int j = 0; j < grid[0].length; j++) { if (grid[i][j] == '1') { count++; dfs(grid, i, j); } } } return count; } public void dfs(char[][] grid, int i, int j) { // validity checking if (i < 0 || j < 0 || i > grid.length - 1 || j > grid[0].length - 1) return; // if current cell is water or visited if (grid[i][j] != '1') return; // set visited cell to '0' grid[i][j] = '0'; // merge all adjacent land dfs(grid, i - 1, j); dfs(grid, i + 1, j); dfs(grid, i, j - 1); dfs(grid, i, j + 1); } /* * 212. Word Search II 12.17 by Mingyang 这么做可以在word * search的基础之上做,但是效率不高,所以后面可以用tire tree */ public List findWords(char[][] board, String[] words) { ArrayList result = new ArrayList (); int m = board.length; int n = board[0].length; for (String word : words) { boolean flag = false; boolean[][] visited = new boolean[m][n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { char[][] newBoard = new char[m][n]; for (int x = 0; x < m; x++) for (int y = 0; y < n; y++) newBoard[x][y] = board[x][y]; if (dfs4(newBoard, word, i, j, 0, visited)) { flag = true; } } } if (flag) { result.add(word); } } return result; } /* * Simplify Path 12.3 by Mingyang 当遇到“/../"则需要返回上级目录,需检查上级目录是否为空。 * * 当遇到"/./"则表示是本级目录,无需做任何特殊操作。 * * 当遇到"//"则表示是本级目录,无需做任何操作。 * * 当遇到其他字符则表示是文件夹名,无需简化。 * * 当字符串是空或者遇到”/../”,则需要返回一个"/"。 * * 当遇见"/a//b",则需要简化为"/a/b"。 所以我们这里遇到简化的时候,先split为//之间的值。然后用两个堆栈来解决,因为方向是反的 * 当字符串为空或者为".",不做任何操作。 * * 当字符串不为"..",则将字符串入栈。 * * 当字符串为"..", 则弹栈(返回上级目录)。 */ public String simplifyPath(String path) { if (path == null || path.length() == 0) return path; Stack stack = new Stack (); String[] list = path.split("/"); for (int i = 0; i < list.length; i++) { if (list[i].equals(".") || list[i].length() == 0) continue; else if (!list[i].equals("..")) stack.push(list[i]); else { if (!stack.isEmpty()) stack.pop(); } } StringBuilder res = new StringBuilder(); Stack temp = new Stack (); while (!stack.isEmpty()) temp.push(stack.pop()); while (!temp.isEmpty()) res.append("/" + temp.pop()); if (res.length() == 0) res.append("/"); return res.toString(); } /* * Additive Number 1.2 by Mingyang */ public boolean isAdditiveNumber(String s) { int n = s.length(); for (int i = 1; i < n; i++) { for (int j = i + 1; j < n; j++) { long a = parse(s.substring(0, i)); long b = parse(s.substring(i, j)); if (a == -1 || b == -1) continue; if (dfs(s.substring(j), a, b)) return true; } } return false; } boolean dfs(String s, long a, long b) { if (s.length() == 0) return true; for (int i = 1; i <= s.length(); i++) { long c = parse(s.substring(0, i)); if (c == -1) continue; if (c - a == b && dfs(s.substring(i), b, c)) { return true; } } return false; } long parse(String s) { if (!s.equals("0") && s.startsWith("0")) return -1; long result = 0; try { result = Long.parseLong(s); } catch (NumberFormatException e) { return -1; } return result; }}