backtracking经典题选编(2)
思路:
- 从上往下,一行一行填
- 某一行元素逐个判断,valid则填入并继续探索下一行(,否则跳过,直接探索该行下一个元素)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| import java.util.ArrayList; import java.util.List;
public class LeetCode51 { List<List<String>> res; public List<List<String>> solveNQueens(int n) { this.res=new ArrayList<>(); char[][] board=new char[n][n]; for (int i = 0; i < board.length; i++) { for (int j = 0; j < board[0].length; j++) { board[i][j]='.'; } } backtracking(board,n,0); return this.res; }
List<String> toList(char[][] board){ List<String> list=new ArrayList<>(); for (int i = 0; i < board.length; i++) { StringBuilder sb=new StringBuilder(); for (int j = 0; j < board[0].length; j++) { sb.append(board[i][j]); } list.add(sb.toString()); } return list; }
void backtracking(char[][] board,int n, int row){ if(row==n){ List<String> list=toList(board); this.res.add(new ArrayList<>(list)); return; }
for (int i = 0; i < n; i++) { if(valid(board,n,row,i)){ board[row][i]='Q'; backtracking(board,n,row+1); board[row][i]='.'; } } }
boolean valid(char[][] board,int n,int row,int col){ for (int i = 0; i < row; i++) { if(board[i][col]=='Q'){ return false; } } int r=row-1; int c=col-1; while(r>=0 && c>=0){ if(board[r][c]=='Q'){ return false; } r--; c--; } r=row-1; c=col+1; while(r>=0 && c<n){ if(board[r][c]=='Q'){ return false; } r--; c++; } return true; } }
|
本题难点:
- 切割问题可以抽象为组合问题
- 如何模拟那些切割线
- 切割问题中递归如何终止
- 在递归循环中如何截取子串
- 如何判断回文
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| List<List<String>> res; public List<List<String>> partition(String s) { this.res=new ArrayList<>(); backtracking(new ArrayList<>(),s,0); return this.res; }
boolean isPalindrome(String s, int start, int end){ while(start<end){ if(s.charAt(start)!=s.charAt(end)){ return false; } start++; end--; } return true; }
void backtracking(List<String> list,String s,int start){ if(start>=s.length()){ this.res.add(new ArrayList<>(list)); return; } for (int i = start; i < s.length(); i++) { if(isPalindrome(s,start,i)){ list.add(s.substring(start,i+1)); backtracking(list,s,i+1); list.remove(list.size()-1); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| import java.util.ArrayList; import java.util.LinkedList; import java.util.List;
public class LeetCode93 { public List<String> restoreIpAddresses(String s) { List<String> res=new ArrayList<>(); backtracking(res,new LinkedList<>(),s,3,0); return res; }
boolean isValid(String s, int start, int end){ if(start<end && s.charAt(start)=='0' || end-start>=3){ return false; } int sum=0; int digit=1; while(start<=end){ sum+=(s.charAt(end)-'0')*digit; digit*=10; end--; } return sum<=255 ? true : false; }
void backtracking(List<String> res, LinkedList<String> list, String s, int dot, int start){ if(start>=s.length()){ return; } if(dot==0){ if(isValid(s,start,s.length()-1)){ StringBuilder sb=new StringBuilder(); for (String s1 : list) { sb.append(s1); } sb.append(s.substring(start,s.length())); res.add(new String(sb)); } return; } for (int i = start; i < s.length(); i++) { if(isValid(s,start,i)){ list.add(s.substring(start,i+1)); list.add("."); backtracking(res,list,s,dot-1,i+1); list.removeLast(); list.removeLast(); } } } }
|
思考:为什么这种void写法不行,一定要将helper设为boolean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void backtracking(char[][] board,int row,int col,int index){ if(index>=row*col){ return; } int i=index/row; int j=index%row; if(board[i][j]!='.'){ backtracking(board,row,col,index+1); }else{ for (char c = '1'; c <= '9'; c++) { if(valid(board,c,i,j)){ board[i][j]=c; backtracking(board,row,col,index+1); board[i][j]='.'; } } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| public void solveSudoku(char[][] board) { solveSudokuHelper(board); }
private boolean solveSudokuHelper(char[][] board){ for (int i = 0; i < 9; i++){ for (int j = 0; j < 9; j++){ if (board[i][j] != '.'){ continue; } for (char k = '1'; k <= '9'; k++){ if (isValidSudoku(i, j, k, board)){ board[i][j] = k; if (solveSudokuHelper(board)){ return true; } board[i][j] = '.'; } } return false; } } return true; }
private boolean isValidSudoku(int row, int col, char val, char[][] board){ for (int i = 0; i < 9; i++){ if (board[row][i] == val){ return false; } } for (int j = 0; j < 9; j++){ if (board[j][col] == val){ return false; } } int startRow = (row / 3) * 3; int startCol = (col / 3) * 3; for (int i = startRow; i < startRow + 3; i++){ for (int j = startCol; j < startCol + 3; j++){ if (board[i][j] == val){ return false; } } } return true; }
|
注意去重逻辑:不能对原数组排序,所以排序+visited数组老方法不行!
不仅是和该元素前一个相比较,而是和该元素之前的全部元素相比较
新去重思路:在每一层开一个map,每一层的相同元素只有第一个元素保留,其余都剪枝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public List<List<Integer>> findSubsequences(int[] nums) { List<List<Integer>> res=new ArrayList<>(); backtracking(res,new LinkedList<>(),nums,0); return res; }
void backtracking(List<List<Integer>> res, LinkedList<Integer> list, int[] nums,int start ){ if(start>=nums.length){ return; } Map<Integer,Integer> map=new HashMap<>(); for (int i = start; i < nums.length; i++) { if(!list.isEmpty() && list.getLast()>nums[i]){ continue; } if(map.getOrDefault(nums[i],0)>=1){ continue; } list.add(nums[i]); map.put(nums[i],map.getOrDefault(nums[i],0)+1); if(list.size()>=2){ res.add(new LinkedList<>(list)); } backtracking(res,list,nums,i+1); list.removeLast(); } }
|
优化思路:由于元素范围有限,可用数组代替map
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class LeetCode491 { List<List<Integer>> res; LinkedList<Integer> list; public List<List<Integer>> findSubsequences(int[] nums) { res=new ArrayList<>(); list=new LinkedList<>(); backtracking(nums,0); return res; }
void backtracking(int[] nums, int start){ if(list.size()>=2){ res.add(new LinkedList<>(list)); } if(start==nums.length){ return; } boolean[] visited=new boolean[201]; for (int i = start; i < nums.length; i++) { if(visited[nums[i]+100]){ continue; } if(list.size()==0 || nums[i]>=list.getLast()){ list.add(nums[i]); visited[nums[i]+100]=true; backtracking(nums,i+1); list.removeLast(); } } } }
|
You must use all the tickets once and only once.
list.get(0)=”JFK” 起点给定
回溯终止条件:所有票都used, 即 list.length==tickets.length+1
哪些票valid: used==false && 起点.equals(list.getLast())
结果itineray不止一条:取字典序最小的一条
超时的笨方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| List<String> res; public List<String> findItinerary(List<List<String>> tickets) { this.res=new ArrayList<>(); List<String> list=new ArrayList<>(); backtracking(list,tickets,new boolean[tickets.size()]); return res; }
void backtracking( List<String> list, List<List<String>> tickets, boolean[] used){ if(list.size()==tickets.size()+1){ if(res.size()==0 || better(list,res)){ res=new ArrayList<>(list); } return; } if(list.size()==0){ for (int i = 0; i < tickets.size(); i++) { if(tickets.get(i).get(0).equals("JFK")){ list.add("JFK"); list.add(tickets.get(i).get(1)); used[i]=true; backtracking(list,tickets,used); list.remove(list.size()-1); list.remove(list.size()-1); used[i]=false; } } }else{ for (int i = 0; i < used.length; i++) { if(used[i]){ continue; } String last=list.get(list.size()-1); if(tickets.get(i).get(0).equals(last)){ list.add(tickets.get(i).get(1)); used[i]=true; backtracking(list,tickets,used); list.remove(list.size()-1); used[i]=false; } } }
}
boolean better(List<String> l1,List<String> l2){ StringBuilder sb1=new StringBuilder(); StringBuilder sb2=new StringBuilder(); for (String s : l1) { sb1.append(s); } for (String s : l2) { sb2.append(s); } if(sb1.toString().compareTo(sb2.toString())<0){ return true; }else{ return false; } }
|
笨方法不行,考虑先排序,再backtracking, 保证“小”票先被used
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| List<String> res; boolean found; public List<String> findItinerary(List<List<String>> tickets) { tickets.sort(new Comparator<List<String>>() { @Override public int compare(List<String> o1, List<String> o2) { return !o1.get(0).equals(o2.get(0)) ? o1.get(0).compareTo(o2.get(0)) : o1.get(1).compareTo(o2.get(1)); } }); this.res=new ArrayList<>(); List<String> list=new ArrayList<>(); backtracking(list,tickets,new boolean[tickets.size()]); return res; }
void backtracking( List<String> list, List<List<String>> tickets, boolean[] used){ if(list.size()==tickets.size()+1){ res=new ArrayList<>(list); found=true; return; } if(list.size()==0){ for (int i = 0; i < tickets.size(); i++) { if(tickets.get(i).get(0).equals("JFK")){ list.add("JFK"); list.add(tickets.get(i).get(1)); used[i]=true; backtracking(list,tickets,used); list.remove(list.size()-1); list.remove(list.size()-1); used[i]=false; } } }else{ for (int i = 0; i < used.length; i++) { if(found){ break; } if(used[i]){ continue; } String last=list.get(list.size()-1); if(tickets.get(i).get(0).equals(last)){ list.add(tickets.get(i).get(1)); used[i]=true; backtracking(list,tickets,used); list.remove(list.size()-1); used[i]=false; } } } }
|
320. Generalized Abbreviation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| List<String> res=new ArrayList<>(); Set<String> set=new HashSet<>(); LinkedList<String> list=new LinkedList<>(); public List<String> generateAbbreviations(String word) { backtracking(word,0); return res; }
void backtracking(String word, int start){ if(start==word.length()){ StringBuilder sb=new StringBuilder(); for (String s : list) { sb.append(s); } String s=sb.toString(); if(!set.contains(s)){ set.add(s); res.add(s); } return; } boolean avoid=false; if(!list.isEmpty()){ String last=list.getLast(); if(Character.isDigit(last.charAt(0))){ avoid=true; } } for (int i = start; i < word.length(); i++) { String sub=word.substring(start,i+1); list.add(sub); backtracking(word,i+1); list.removeLast(); if(!avoid){ int len=sub.length(); list.add(Integer.toString(len)); backtracking(word,i+1); list.removeLast(); } } }
|
254. Factor Combinations
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| List<List<Integer>> res; List<Integer> list; public List<List<Integer>> getFactors(int n) { res=new ArrayList<>(); list=new ArrayList<>(); backtracking(n,2); return res; }
void backtracking(int cur, int start){ if(cur==1){ if(list.size()>1){ res.add(new ArrayList<>(list)); } return; } for (int i = start; i <= cur; i++) { if(cur%i==0) { list.add(i); backtracking(cur / i, i); list.remove(list.size() - 1); } } }
|