改了非常非常非常久!!不知道为什么用set去重就是没成功。在遍历的时候剪枝感觉没那么容易想到
class Solution {ArrayList> res = new ArrayList>();ArrayList num = new ArrayList();public List> combine(int n, int k) {//21dfs(n,1,k);return res;}public void dfs(int n,int index,int k){if(num.size()==k){//长度够了res.add(new ArrayList<>(num));return;}for(int i=index;i<=n;i++){if(num.contains(i)) continue;num.add(i);dfs(n,i+1,k);//注意不是index+1 下一轮搜索,设置的搜索起点要加 1,因为组合数理不允许出现重复的元素//这样后面的元素一定比之前的元素来得大!!!!num.remove(num.size()-1);}}
}
一开始看成是三数之和,后来发现可以是任意k个数量,所以还是用回溯
没想到还是一次过了
class Solution {ArrayList> res = new ArrayList>();List num = new ArrayList<>();public List> combinationSum3(int k, int n) {//44-52 三数之和 但是是k个数!!52-00dfs(1,k,n,0);return res;}public void dfs(int count,int k,int n,int sum){if(sum>n) return;//减枝if(num.size()==k&&sum==n){res.add(new ArrayList<>(num));return;}for(int i=count;i<=9;i++){num.add(i);dfs(i+1,k,n,sum+i);num.remove(num.size()-1);}}
}
时间复杂度好高
一开始对映射无从下手,还在找字母和数字之间的规律,看了题解发现可以用String[]数组存,用对应数字做下标就可以方便找到。然后就是回溯的常规写法了,就是对应数字的遍历要仔细一些
class Solution {List res = new ArrayList<>();StringBuilder tmp = new StringBuilder();public List letterCombinations(String digits) {//30-54if(digits.length()==0) return res;String[] map = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};dfs(map,digits,0);return res;}public void dfs(String[] map, String s, int index){if(tmp.length()==s.length()){res.add(new String(tmp.toString()));return;}for(int i=0;i
class Solution {ArrayList> res = new ArrayList>();List tmp = new ArrayList();public List> combinationSum(int[] candidates, int target) {//26-37Arrays.sort(candidates);dfs(candidates,target,0);return res;}public void dfs(int[] candidates, int target,int index){if(target==0){res.add(new ArrayList(tmp));return;}for(int i=index;iif(candidates[i]>target) break;tmp.add(candidates[i]);dfs(candidates,target-candidates[i],i);//注意是i 这样可以重复使用,并且后面的都不会比之前小,不需要排序再去重tmp.remove(tmp.size()-1);}}
}
class Solution {ArrayList> res = new ArrayList>();List tmp = new ArrayList();public List> combinationSum2(int[] candidates, int target) {//10Arrays.sort(candidates);// List dfs(candidates,target,0);return res;}public void dfs(int[] candidates, int target, int index){if(target==0){res.add(new ArrayList(tmp));return;}for(int i=index;iif(candidates[i]>target) break;//要对同一树层使用过的元素进行跳过if(i>index&&candidates[i-1]==candidates[i]) continue;//一开始写的是//if(i>0&&candidates[i-1]==candidates[i]&&!tmp.contains(candidates[i-1])) continue;tmp.add(candidates[i]);dfs(candidates,target-candidates[i],i+1);tmp.remove(tmp.size()-1);}}
}
debug了很久!!我原来的判断方式也就是注释掉的那行思路是没问题,问题在于contains函数的判断
[2,2,2] target=4这样的例子就会出来两个[2,2].因为contains判断的是值的内容!!也就是第几个2在里面都会判断出包含,而我希望不同索引的2是有区别的,所以要这样判断就要用索引去判断!!还是用答案中的方法更简洁!!!
//要对同一树层使用过的元素进行跳过if(i>index&&candidates[i-1]==candidates[i]) continue;
感觉这题没有太简单,代码独立完成之后测试出现了问题,比如会出现b这样的答案,手动debug才改了过来(注释掉的for循环),要是有这个循环,i就有可能从中间开始,答案就不连续,改完bug总共花了半个小时,还是需要再练习
class Solution {ArrayList> res = new ArrayList>();List tmp = new ArrayList();public List> partition(String s) {//37-08dfs(s,0);return res;}public void dfs(String s, int index){if(index==s.length()){res.add(new ArrayList(tmp));return;}// for(int i = index;i//可以遍历的长度int i = index;for(int j=1;j<=s.length()-i;j++){if(isH(s.substring(i,i+j))){tmp.add(s.substring(i,i+j));dfs(s,i+j);tmp.remove(tmp.size()-1);}}// }}//判断是否是回文串public boolean isH(String s){char[] c = s.toCharArray();for(int i=0,j=c.length-1;i<=j;i++,j--){if(c[i]!=c[j]) return false;}return true;}}
改一下用通用的写法
class Solution {ArrayList> res = new ArrayList>();List tmp = new ArrayList();public List> partition(String s) {//37-08dfs(s,0);return res;}public void dfs(String s, int index){if(index==s.length()){res.add(new ArrayList(tmp));return;}for(int i = index;iif(isH(s.substring(index,i+1))){tmp.add(s.substring(index,i+1));dfs(s,i+1);tmp.remove(tmp.size()-1);}}}public boolean isH(String s){char[] c = s.toCharArray();for(int i=0,j=c.length-1;i<=j;i++,j--){if(c[i]!=c[j]) return false;}return true;}}
这题写了很久没写出来!!要重点关注
class Solution {List res = new ArrayList();StringBuilder tmp = new StringBuilder();public List restoreIpAddresses(String s) {//21dfs(s,0,0);return res;}public void dfs(String s, int index, int num){//总共4段if(index==s.length()&&num==4){res.add(new String(tmp));return;}if(index==s.length()||num==4) return; //点过多或过少都不行for(int i=index;i //从index开始,最多3位int data = Integer.parseInt(s.substring(index,i+1));if(data>255||data<0) break;//数据范围不对直接返回if(i-index>0&&s.charAt(index)=='0') continue;//第一位为0并且这段长度>1tmp.append(s.substring(index,i+1));if(num<3) tmp.append(".");//最后一段不用加点dfs(s,i+1,num+1); tmp.delete(index + num, tmp.length());//i + number + 2//tmp.delete(tmp.length()-i-2+index,tmp.length()); }}
}
这题写得比较快,也不需要减枝
class Solution {ArrayList> res = new ArrayList>();List tmp = new ArrayList();public List> subsets(int[] nums) {//03-13for(int i=0;i<=nums.length;i++){//有几位dfs(nums,0,i); }return res;}public void dfs(int[] nums, int index, int num){if(tmp.size()==num){res.add(new ArrayList(tmp));return;}for(int i=index;itmp.add(nums[i]);dfs(nums,i+1,num);tmp.remove(tmp.size()-1);}}
}
就是结合了79子集和20组合总和2,加个树层去重即可
class Solution {ArrayList> res = new ArrayList>();List tmp = new ArrayList();public List> subsetsWithDup(int[] nums) {//40-46Arrays.sort(nums);for(int i=0;i<=nums.length;i++){dfs(nums,0,i);}return res;}public void dfs(int[] nums, int index, int num){if(tmp.size()==num){res.add(new ArrayList(tmp));return;}for(int i=index;iif(i>index&&nums[i-1]==nums[i]) continue;tmp.add(nums[i]);dfs(nums,i+1,num);tmp.remove(tmp.size()-1);}}
}
剪枝没想出来!!这题不能排序!!可以利用标记数组
class Solution {ArrayList> res = new ArrayList>();List tmp = new ArrayList();public List> findSubsequences(int[] nums) {//49// Arrays.sort(nums);dfs(nums,0);return res;}public void dfs(int[] nums, int index){if(tmp.size()>1)res.add(new ArrayList(tmp));// 注意这里不要加return,要取树上的节点int[] used = new int[201];//nums[i]+100 定义在dfs内同一层就会公用一个used数组,下层used数组又清零,所以实现了数层减枝,而树叶不减Arrays.fill(used,0);for(int i=index;i//if(i>index&&nums[i-1]==nums[i]) continue;if(used[nums[i] + 100] == 1) continue;if(tmp.size()==0||(tmp.size()>0&&nums[i]>=tmp.get(tmp.size()-1))){used[nums[i] + 100] = 1;tmp.add(nums[i]);dfs(nums,i+1);tmp.remove(tmp.size()-1);}}}
}
不用set去重,直接剪枝,数组标记每个位置的数字是否被使用过而不是统计每个数字出现的次数,这样在剪枝判断的时候就可以利用上一个相同的数字是否出现过来判断。
class Solution {ArrayList> res = new ArrayList>();List tmp = new ArrayList();int[] count = new int[10];public List> permuteUnique(int[] nums) {//59-16Arrays.sort(nums);for(int i=0;icount[i]=1;}dfs(nums,0);return res;}public void dfs(int[] nums, int index){if(index==nums.length){res.add(new ArrayList(tmp));return;}for(int i=0;iif(i>0&&nums[i-1]==nums[i]&&count[i-1]==1) continue;if(count[i]<=0) continue;tmp.add(nums[i]);count[i]=0;dfs(nums,index+1);tmp.remove(tmp.size()-1);count[i]=1;}}
}
没看题解真的想不到和回溯有什么关系,还是看了别人的代码再自己手打的,还是从这个代码中学到了不少,compareTo函数可以用来比较String的大小,写法和其他回溯没有什么区别,就是多用来判断如果找到了结果就返回,不用继续往下,因为先对tickets排序过了,先找到的一定是符合要求的字典序最小的,所以找到了就不用再找了
class Solution {List res = new ArrayList();List path = new ArrayList();boolean used[] = new boolean[301];public List findItinerary(List> tickets) {//17Collections.sort(tickets,(a,b)->a.get(1).compareTo(b.get(1)));//compareTo函数是对string的比较?//第一个本来就是要匹配的,所以只要比较第二个Arrays.fill(used,false);path.add("JFK");dfs(tickets);return res;}public boolean dfs(List> tickets){if(path.size()==tickets.size()+1){res = new ArrayList(path);return true;}for(int i=0;iif(!used[i]&&path.get(path.size()-1).equals(tickets.get(i).get(0))){path.add(tickets.get(i).get(1));used[i] = true;if(dfs(tickets)) return true;//找到了就不用再找path.remove(path.size()-1);used[i] = false;}}return false; }
}
这个写法是最简洁的,特别是判断是否合法的函数,第二次写还是写错了
class Solution {ArrayList> res = new ArrayList>();List tmp = new ArrayList();int[] pos = new int[9];//表示i行y列存放了public List> solveNQueens(int n) {//55-13dfs(n, 0);return res;}public void dfs(int n, int index){if(index==n){res.add(new ArrayList(tmp));return;}for(int j=0;j//index表示的就是行,不用再一个行的for循环if(isValid(index,j)){pos[index] = j;String s = "";for(int k = 0;kif(k!=j) s+=".";else s+="Q";}tmp.add(new String(s));dfs(n,index+1);tmp.remove(tmp.size()-1);pos[index] = 0;}}}public boolean isValid(int row, int col){for(int i=0;i//第二次写还是写错//只需要遍历到row之前的//对角线的判断是Math.abs(i-row)==Math.abs(col-pos[i])而不一定都等于1if(i==row||col==pos[i]||Math.abs(i-row)==Math.abs(col-pos[i]))return false;}return true;}
}
没写出来,一直想在dfs里传index,发现不太行,还是需要再看一下这个写法
class Solution {public void solveSudoku(char[][] board) {//20dfs(board);}public boolean dfs(char[][] board){for(int row=0;row<9;row++){//遍历行for(int col=0;col<9;col++){//遍历列if(board[row][col]=='.'){for(char num = '1';num<='9';num++){//遍历数字if(isValid(board,row,col,num)){board[row][col] = num;if(dfs(board)) return true;board[row][col] = '.';}}return false;}}}return true;}public boolean isValid(char[][] board, int row, int col, char num){//比较同行for(int i=0;i<9;i++){if(board[row][i]==num) return false;}//比较同列for(int i=0;i<9;i++){if(board[i][col]==num) return false;}//九宫格内int startRow = (row/3)*3;int startCol = (col/3)*3;for(int i=startRow;ifor(int j =startCol;jif(board[i][j]==num) return false;}}return true;}
}
下一篇:结对编程——一路忘川