leetcode做题总结,回溯法(N-Queens, N-QueensII,Combination SumI&II,wordbreak II, SubsetsI&II)
来源:互联网 发布:网络捕鱼代理赚钱吗 编辑:程序博客网 时间:2024/05/18 00:02
最近打算对回溯法做一总结,回溯法的思路简单来说就是去试,把问题的可能解变成一棵决策树,然后用递归向下探路,如果走不通就向上回溯,中间可以用条件判断进行剪枝,避免不必要的历遍。
首先是回溯法最经典的N皇后问题,思路并不麻烦,从第一行开始依次设一个点为Q,然后向第二行前进,历遍每一个点,如果某个点可行就继续前进。
public class Solution { //checking if a specific position in row row is valid. private boolean check(int row, int[] queenpositions){ for(int i=0;i<row;i++){ if(queenpositions[row]==queenpositions[i] || Math.abs(queenpositions[row]-queenpositions[i])==row-i) return false; } return true; } private void queen(int n, int row, int[] queenpositions, ArrayList<String[]> l){ if(row == n){ String[] res = new String[n]; for(int i=0;i<n;i++){ String strRow=""; for(int j=0;j<n;j++){ if(queenpositions[i]==j) strRow+="Q"; else strRow+="."; } res[i] = strRow; } l.add(res); }else{ for(int i=0;i<n;i++){ queenpositions[row] = i; if(check(row,queenpositions)){ queen(n,row+1,queenpositions,l); } } } } public List<String[]> solveNQueens(int n) { ArrayList<String[]> l = new ArrayList<String[]>(); //Storing the queen position of each row. int[] queenpositions = new int[n]; queen(n,0,queenpositions, l); return l; }}
之后是N皇后II,就是计算N皇后有几种解法
public class Solution { int sum=0; //checking if a specific position in row row is valid. private boolean check(int row, int[] queenpositions){ for(int i=0;i<row;i++){ if(queenpositions[row]==queenpositions[i] || Math.abs(queenpositions[row]-queenpositions[i])==row-i) return false; } return true; } private void queen(int n, int row, int[] queenpositions){ if(row == n) sum++; else{ for(int i=0;i<n;i++){ queenpositions[row] = i; if(check(row,queenpositions)){ queen(n,row+1,queenpositions); } } } } public int totalNQueens(int n) { int[] queenpositions = new int[n]; queen(n,0,queenpositions); return sum; }}
下一题是Letter Combinations of a Phone Number . 典型的backtrace,没什么可说的。
public class Solution { private void phone(int k, int n, String[] input,char[] output,ArrayList<String> l){ if(k==n){ String res=""; //The first element of output is "" for(int i=1;i<n;i++){ res+=output[i]; } l.add(res); }else{ if(Integer.parseInt(input[k])>0&&Integer.parseInt(input[k])<7){ for(int i=97+3*(Integer.parseInt(input[k])-2);i<97+3*(Integer.parseInt(input[k])-1);i++){ output[k]=(char)i; phone(k+1, n, input,output, l); } }else if(Integer.parseInt(input[k])==7){ for(int i=112;i<116;i++){ output[k]=(char)i; phone(k+1, n, input,output, l); } }else if(Integer.parseInt(input[k])==9){ for(int i=119;i<123;i++){ output[k]=(char)i; phone(k+1, n, input,output, l); } }else{ for(int i=116;i<119;i++){ output[k]=(char)i; phone(k+1, n, input,output, l); } } } } public List<String> letterCombinations(String digits) { ArrayList<String> l = new ArrayList<String>(); //if string is empty, return l; if(digits.length()<1){ String res=""; l.add(res); return l; } //important!! The first element of input is "". Thus we should start from index=1 String[] input = digits.split(""); char[] output=new char[input.length]; phone(1,input.length,input,output,l); return l; }}
在这道题的时候,学到了点新的知识,不是对字符串用split("")来分,返回的数组第一个元素为空。还有就是容器和数组一样,传入函数即可直接修改,函数不用返回容器,除非是在函数内new的。
下一道题是Combination Sum,这道题也是同样的思路,要提一下的是如果往一个listA里添加另一个ListB,这是最好复制一个新的list加进去,否则如果外部对这个listB修改,会导致A里面的元素改变!新建的方法为new ArrayList(oldlist);
public class Solution { private void combi(int left, int sum,int[] candidates, ArrayList<Integer> res,ArrayList<List<Integer>> l,int target){ if(sum==target){ //if we need a new ArrayList to be added to l. Because if we change the res2 which will also be changed in l. ArrayList<Integer> res2= new ArrayList<Integer>(res); l.add(res2); }else if(sum<target){ for(int i=left;i<candidates.length;i++){ res.add(candidates[i]); sum+=candidates[i]; combi(i,sum,candidates,res,l,target); sum-=candidates[i]; res.remove(res.size()-1); } } } public List<List<Integer>> combinationSum(int[] candidates, int target) { ArrayList<List<Integer>> l = new ArrayList<List<Integer>>(); if(candidates.length==0) return l; Arrays.sort(candidates); ArrayList<Integer> res= new ArrayList<Integer>(); combi(0,0,candidates,res,l,target); return l; }}
下一道题是Combination Sum II,这道题和迁移到差不多,只是在递归的时候修改开始位置防止重复,还有一个问题是listA里的ListB有可能会出项重复,这样要在加入是用contains进行判断。其实按理说上一题也会有这样的问题,只是testcase里没有所以没检测出来。
public class Solution { private void combi(int left, int sum,int[] candidates, ArrayList<Integer> res,ArrayList<List<Integer>> l,int target){ if(sum==target){ if(l.contains(res)); else{ ArrayList<Integer> res2= new ArrayList<Integer>(res); l.add(res2); } }else if(sum<target){ for(int i=left;i<candidates.length;i++){ res.add(candidates[i]); sum+=candidates[i]; combi(i+1,sum,candidates,res,l,target); sum-=candidates[i]; res.remove(res.size()-1); } } } public List<List<Integer>> combinationSum2(int[] num, int target) { ArrayList<List<Integer>> l = new ArrayList<List<Integer>>(); if(num.length==0) return l; Arrays.sort(num); ArrayList<Integer> res= new ArrayList<Integer>(); combi(0,0,num,res,l,target); return l; }}
Update 2015/08/28:上面的思路正确但是写法繁琐,对于需要递归累加求target得题目,相较于上面的使用sum记录,更好的是对减小target, 减到零就说明相等了。
public class Solution { /** * @param candidates: A list of integers * @param target:An integer * @return: A list of lists of integers */ public void compute( ArrayList<List<Integer>>res, ArrayList<Integer> tmp, int[] num, int s, int target){ if (target == 0){ if (!res.contains(tmp)) res.add(new ArrayList<Integer>(tmp)); } if (target < 0) return; for (int i = s; i < num.length; i++){ tmp.add(num[i]); compute(res, tmp, num, i, target - num[i]); tmp.remove(tmp.size() - 1); } } public List<List<Integer>> combinationSum(int[] candidates, int target) { // write your code here ArrayList<List<Integer>> res = new ArrayList<List<Integer>>();ArrayList<Integer> tmp = new ArrayList<Integer>();Arrays.sort(candidates);compute(res, tmp, candidates, 0, target);return res; }}
public class Solution { /** * @param num: Given the candidate numbers * @param target: Given the target number * @return: All the combinations that sum to target */ public void compute( ArrayList<List<Integer>>res, ArrayList<Integer> tmp, int[] num, int s, int target){ if (target == 0){ if (!res.contains(tmp)) res.add(new ArrayList<Integer>(tmp)); } if (target < 0) return; for (int i = s; i < num.length; i++){ tmp.add(num[i]); compute(res, tmp, num, i + 1, target - num[i]); tmp.remove(tmp.size() - 1); } } public List<List<Integer>> combinationSum2(int[] num, int target) { // write your code here ArrayList<List<Integer>> res = new ArrayList<List<Integer>>();ArrayList<Integer> tmp = new ArrayList<Integer>();Arrays.sort(num);compute(res, tmp, num, 0, target);return res; }}
ArrayList<ArrayList<String>>[] sa = new ArrayList[2];sa[0] = new ArrayList<ArrayList<String>>();
这道题的解法是
public class Solution { private void getword(int start, String s, String res,ArrayList<String> l,Set<String> dict){ if(start==s.length()){ l.add(res.substring(1)); }else{ for(int i=start;i<s.length();i++){ if(dict.contains(s.substring(start,i+1))){ String newres=res+" "+s.substring(start,i+1); getword(i+1,s,newres,l,dict); } } } } public List<String> wordBreak(String s, Set<String> dict) { ArrayList<String> l = new ArrayList(); if(s.length()==0) return l; String res=""; int n = s.length(); boolean[] dp = new boolean[n+1]; dp[0] = true; for (int i=1; i<=n; i++) { if (dict.contains(s.substring(0, i))) { dp[i] = true; continue; } for (int j=0; j<i; j++) { if (dp[j] && dict.contains(s.substring(j, i))) { dp[i] = true; } } } if (dp[n] == false) return l; getword(0,s,res,l,dict); return l; }}
下面是Subsets, 这道题不知道为什么会被归到动态规划里去,但是我用回溯法搞定了。题目是从一个set里提取出来子Set,然后从单元素到多元素向下递归。
public class Solution { private void getsub(int left, int[] S,ArrayList<Integer> res, ArrayList<List<Integer>> l){ if(left<S.length){ for(int i=left; i<S.length; i++){ res.add(S[i]); //never forget add a new ArrayList to outter ArrayList! ArrayList<Integer> restmp = new ArrayList<Integer>(res); l.add(restmp); getsub(i+1, S, res, l); //never forget remove the element for backtracing. res.remove(res.size()-1); } } } public List<List<Integer>> subsets(int[] S) { ArrayList<List<Integer>> l = new ArrayList<List<Integer>>(); if(S.length==0) return l; ArrayList<Integer> res = new ArrayList<Integer>(); //never forget sort! Arrays.sort(S); getsub(0, S, res, l); l.add(new ArrayList<Integer>()); return l; }}
Subsets II 这道题和上道题思路一样,只是由于根Set里可能有重复元素,这种问题之前遇到过好多次,方法是在加入外层list前用contains判断一下即可。
public class Solution { private void getsub(int left, int[] S,ArrayList<Integer> res, ArrayList<List<Integer>> l){ if(left<S.length){ for(int i=left; i<S.length; i++){ res.add(S[i]); ArrayList<Integer> restmp = new ArrayList<Integer>(res); //for array with duplicate element, all we need to do is check if it already exits in outter list if(!l.contains(restmp)) l.add(restmp); getsub(i+1, S, res, l); res.remove(res.size()-1); } } } public List<List<Integer>> subsetsWithDup(int[] num) { ArrayList<List<Integer>> l = new ArrayList<List<Integer>>(); if(num.length==0) return l; ArrayList<Integer> res = new ArrayList<Integer>(); Arrays.sort(num); getsub(0, num, res, l); l.add(new ArrayList<Integer>()); return l; }}
0 0
- leetcode做题总结,回溯法(N-Queens, N-QueensII,Combination SumI&II,wordbreak II, SubsetsI&II)
- 【LeetCode】N-Queens II N皇后问题 回溯法
- LeetCode: N-Queens II
- LeetCode : N-Queens II
- [Leetcode] N-Queens II
- 【leetcode】N-Queens II
- LeetCode: N Queens II
- [LeetCode]N-Queens II
- LeetCode - N-Queens II
- LeetCode:N-Queens II
- 【leetcode】N-Queens II
- LeetCode:N-Queens II
- Leetcode: N-Queens II
- leetcode N-Queens II
- LeetCode | N-Queens II
- Leetcode: N-Queens II
- LeetCode N-Queens II
- Leetcode N-Queens II
- jsp页面静态化技术资料收集
- 打包
- 开始并不意味着成功
- CF - 494B - 字符串预处理 + DP
- 字符编码总结
- leetcode做题总结,回溯法(N-Queens, N-QueensII,Combination SumI&II,wordbreak II, SubsetsI&II)
- php dom操作存储xml节点
- 10gen工程师谈MongoDB组合索引的优化
- 哇噻夫微商论坛开启了
- MongoDB 数据文件备份与恢复
- 安卓面试技术点之xmpp知识准备
- Python中常用的一些操作总结(未完待续)update @ 2017-1-6
- HTB分层令牌桶排队规则分析
- 数据库索引介绍及使用