FTPrep, 77 Combinations

来源:互联网 发布:淘宝网精品推荐棉拖鞋 编辑:程序博客网 时间:2024/06/05 22:30

这是一大类型的题,对比总结发现就知道:核心是:backtracking的core,应对不同变体的处理方式是:退出条件的设立。

还有一个特点就是他的输出是 list in list 的格式,首先要得到一个关于combination的list,然后在一定判断条件下,把这个valid的list加入结果result中。

TODO:

backtracking就是DFS的一种特例,是走到根节点,这个根节点可以作为结束条件,比如 电话号码,和 generate parens 那道题,当然也可以在中间进行结束,只要配置相应的判断条件就够了。

突然我就有个问题,为什么电话号码和generate parens 那道题,没有backtracking的步骤,照理说,也是同样的DFS过程,都是一直走到树的最左侧那条分支,即从左到右方向的第一个根节点,然后向上回溯,向下继续寻找第二个根节点。依次进行下去。 对比观察,终于发现关键所在。

1. telephone number 和 generate parens 那里的item是string,result的形式是一个list。 而combination的item本身就是一个list,最终结果是 list in list。这第一点是表面上区别,但是也蛮重要,因为item类型不同,其实就意味着 形成的过程/怎么generate item 有所差异。

2, 在DFS中,string的处理是直接在interface的参数列表里进行处理的,因为可以非常方便的通过 str1+“a” 来产生新的对像,所以每一个分支时,item都一个新的对象。相反的,在backtracking中,对item添加元素都在是for() loop里,进行操作,然后把item pass 到interface 的参数列表里,所以这个item 自始至终是一个对象,所以在backtracking 回溯的时候要remove 这个 iteration中加进去的元素,进入到 for() loop 的下一个 iteration中去。

3, 这一点其实是最大的不同,最明显的区别!在MU吃完pizza散步的时候突然意识到这才最本质的区别。在DFS中,是通过index来 逐层 深入的!for() loop是要遍历“该层”的所有分支!!这个分支可以是很多个。而在backtracking的题目里,for() loop 有两个意思:1,把每个元素都作为一个起点,即数的根节点(在第一个for() loop 里遍历元素时)这一点是最本质的,因为第2点是基于第1点的;2,就是遍历每一层(在递归进入下一层时,下一层内部的for loop() ,因为这个interface里已经带有一个上一层的item了)!!那么他的分支是什么呢?又是怎么实现的??分支只有2种,选和不选,其实只有选才有意义,所以在每一层,只有一个操作,就是item.add(xxx),然后就继续到下一层了。进入下一层的interface,什么都没有变,只有item变了 和 层数(更准确的说是树的起始点)变了!!从图形化的角度来说,combination的图形化,是把每个元素都可以看成是一个树的根节点,也就是起始位置,因此可以形成N棵树(每棵树的大小逐渐变小,一开始是4个元素的数,然后是3,然后2,1:[1,2,3,4], [1,2,3], [2,3,4], [3,4,1], [4,1,2], [1,2], []...)以各个元素作为根节点往下延伸的时候,仍然需要递归调用。

对比一下,DFS中 往下一层进入时的interface,变化的值有:层数(仅仅是层数,没有起点,因为这里只有一个棵树一个起点一个根节点)即index,和item(item是直接在interface里生成的,不需要 变量 来维护)。

所以从interface的变化来说还是很类似的,interface里都有控制层数的参数变化,DFS的层数是通过index来控制,BT的层数是通过start来控制。同时没进入下一轮的interface所带的item都是上一轮形成的item,DFS在interface里直接形成,是new variable,BT是调用原来的item,同一个variable,只是其中的元素+1 !但是for() loop的作用上,完全不是一回事。DFS的for是分支for,BT的for是起点for,BT其实没有分支(只有选择才有意义)。

唉,在发现这个对比之前,碰到了两者之间对比和疑问,是有点烦的,因为这两类题目都做了不少了,但还是有些困惑,现在对比发现了其中要义,还是蛮爽的。

先上代码,其实这个代码中可以总结出 backtracking的一类模版解法,<--- TODO

class Solution {    public List<List<Integer>> combine(int n, int k) {        List<List<Integer>> result = new ArrayList<>();        if(n<=0 || k>n) return result;        List<Integer> item = new ArrayList<>();        combination(n, k, result, item, 1);        return result;    }        private void combination(int n, int k, List<List<Integer>> result, List<Integer> item, int start ){        // if(start>n-k+1) return;  // question: why this is not right??        if(item.size()==k){            result.add(new ArrayList<Integer>(item));            return;        }                for(int i=start; i<=n; i++){  // bug: i<=n+1-k;            item.add(i);            combination(n, k, result, item, i+1);  // here I always made the mistake by: combination(n, k, result, item, start+1);            item.remove(item.size()-1);         }    }}// combination type of question, the core is the for() loop for backtracking, special case of DFS!!!// variants and corresponding solutions are in: 1, the checking condition!!! 2, the presentation of the selection pool, which can be array, or here is just a range from 1 to n, [1, n]

Leetcode 讨论区有一个很不错的,关于permutation,combination的各种变体的模版,非常值得参考,因为都是backtracking core + conditioning  !!! I like this short summary, core + conditioning.


phone number 点击打开链接的代码:

public class Solution {      public List<String> letterCombinations(String digits) {          List<String> list = new ArrayList<String>();          if(digits.length()==0) return list;                    HashMap<Character, String> table=new HashMap<>();          table.put('2',"abc");          table.put('3',"def");          table.put('4',"ghi");          table.put('5',"jkl");          table.put('6',"mno");          table.put('7',"pqrs");          table.put('8',"tuv");          table.put('9',"wxyz");          table.put('0',"abc");          table.put('1',"");                    branch(digits, table, 0, list, "");          return list;                }      private void branch(String digits, HashMap<Character, String> table, int index, List<String> list, String item){          if(index==digits.length()) {              list.add(item);              return;          }          else{              String str= table.get(digits.charAt(index));              for(int i=0; i<str.length(); ++i){                  branch(digits, table, index+1, list, item+str.charAt(i));              }              }      }  }  

其实这里的for loop里的str应该改名叫做 branches,这才是真正含义,然后helper function应该叫做dfs

class Solution {    public List<String> letterCombinations(String digits) {        List<String> result = new ArrayList<String>();        if(digits.length()==0) return result;        HashMap<Character, String> table=new HashMap<>();          table.put('2',"abc");          table.put('3',"def");          table.put('4',"ghi");          table.put('5',"jkl");          table.put('6',"mno");          table.put('7',"pqrs");          table.put('8',"tuv");          table.put('9',"wxyz");          table.put('0',"abc");          table.put('1',"");          dfs(digits, table, result, "", 0);        return result;    }        private void dfs(String digits, HashMap<Character, String> table, List<String> result, String item, int index){        if(index==digits.length()){            result.add(item);            return;        }            String branches = table.get(digits.charAt(index));        for(int i=0; i<branches.length(); i++){            dfs(digits, table, result, item+branches.charAt(i), index+1);        }    }}





原创粉丝点击