算法之回溯
精髓就是想象一个蜗牛的角,碰到危险回退回去,再换个方向试探,一般深度尽量用递归做,别怕,递归思想才是王道。
技巧就是只有一个临时变量,只有当临时变量是目标 值的时候才new出来,插到总结果当中去。否则看正规代码时,会卡壳。
自己总结的书写规则,基本上套用是没有问题的。
//参数:下一深度,深度选择和总深度,临时数据,结果
//1.1 符合结果的断枝:把临时数据新创建一份,加入到结果中。
//1.2 看看是否还有任何其他不符合断枝情况。
//2.1 不断枝情况:是否需要处理下0深度。
//2.2 列出所有分支,并每次进行回退。
//非重复的组合,一个深度只出现一个元素,那么可以保证组合中不重复元素,至于怎么分支看情况。
//按键组合。
class Solution { //考察深度优先 private Map<Character,List<Character>> mapchar; public List<String> letterCombinations(String digits) { initlist(); List<String> res=new ArrayList<String>(); if(digits.length()==0) { return res; } else { StringBuilder explorer=new StringBuilder(); DFS(0,digits,explorer,res); return res; } } //参数:下一深度,深度选择和总深度,临时数据,结果 //1 断枝:超过最大深度或无效情况,把临时数据新创建一份,加入到结果中。 //2.1 是否需要处理下0深度。 //2.2 列出所有分支,并每次进行回退。 private void DFS(int depth,String digits,StringBuilder explorer,List<String> res) { if(depth>digits.length())//1 断枝:超过最大深度或无效情况 { res.add(explorer.toString()); } else if(depth<=digits.length()) { if(depth==0)//2.1 是否需要处理下0深度 { DFS(1,digits,explorer,res); } else { List<Character> allChar=getList(digits.charAt(depth-1)); for(int i=0;i<allChar.size();i++)//2.2 列出所有分支,并每次进行回退。 { explorer.append(allChar.get(i)); DFS(depth+1,digits,explorer,res); explorer.delete(explorer.length()-1,explorer.length()); } } } } private List<Character> getList(char a) { return mapchar.get(a); } private void initlist() { mapchar=new HashMap(); List<Character> list2= new ArrayList<Character>(); list2.add('a'); list2.add('b'); list2.add('c'); mapchar.put('2',list2); List<Character> list3= new ArrayList<Character>(); list3.add('d'); list3.add('e'); list3.add('f'); mapchar.put('3',list3); List<Character> list4= new ArrayList<Character>(); list4.add('g'); list4.add('h'); list4.add('i'); mapchar.put('4',list4); List<Character> list5= new ArrayList<Character>(); list5.add('j'); list5.add('k'); list5.add('l'); mapchar.put('5',list5); List<Character> list6= new ArrayList<Character>(); list6.add('m'); list6.add('n'); list6.add('o'); mapchar.put('6',list6); List<Character> list7= new ArrayList<Character>(); list7.add('p'); list7.add('q'); list7.add('r'); list7.add('s'); mapchar.put('7',list7); List<Character> list8= new ArrayList<Character>(); list8.add('t'); list8.add('u'); list8.add('v'); mapchar.put('8',list8); List<Character> list9= new ArrayList<Character>(); list9.add('w'); list9.add('x'); list9.add('y'); list9.add('z'); mapchar.put('9',list9); } }
class Solution { public List<List<Integer>> combinationSum2(int[] candidates, int target) { Arrays.sort(candidates); List<Integer> explorer=new LinkedList<Integer>(); List<List<Integer>> res=new ArrayList<List<Integer>>(); dfs(0,candidates,explorer,0,target,res); return res; } //参数:下一深度,深度选择和总深度,临时数据,结果 //1.1 断枝:下次深度刚刚比最大大一,且符合结果,把临时数据新创建一份,加入到结果中。 //1.2 看看是否还有任何其他断枝情况。 //2.1 不断枝情况:是否需要处理下0深度。 //2.2 列出所有分支,并每次进行回退。 private void dfs(int depth,int[] candidates,List<Integer> explorer,int explorerSum,int target,List<List<Integer>> res) { if(depth<=candidates.length+1 && explorerSum==target)//1 断枝:超过最大深度或无效情况,把临时数据新创建一份,加入到结果中.这里断枝加数据还需要检测 { if(!Check(explorer,res)) { res.add(new ArrayList<Integer>(explorer)); } } else if(depth>candidates.length+1 || explorerSum>target)//1 断枝:超过最大深度或无效情况,把临时数据新创建一份,加入到结果中.这里断枝加数据还需要检测 { } else if(depth<candidates.length+1)//2.2 列出所有分支,并每次进行回退。这里任何分支都有可能断指。 { if(depth==0) { dfs(1,candidates,explorer,0,target,res); } else { explorer.add(candidates[depth-1]); dfs(depth+1,candidates,explorer,explorerSum+candidates[depth-1],target,res); explorer.remove(explorer.size()-1); dfs(depth+1,candidates,explorer,explorerSum,target,res); } } } private boolean Check(List<Integer> explorer,List<List<Integer>> res) { boolean check=false; List<Integer> explorer2=new ArrayList<Integer>(explorer); Collections.sort(explorer2); for(int i=0;i<res.size();i++) { List<Integer> cc2=new ArrayList<Integer>(res.get(i)); Collections.sort(cc2); if(cc2.size()==explorer2.size()) { int k=0; for(k=0;k<cc2.size();k++) { if(cc2.get(k)!=explorer2.get(k)) { break; } } if(k==res.get(i).size()) { check=true; break; } } } return check; } }