写这个系列是由于记念一下去年的今天,就是2015年的9月14号,刷题第一天,今天是一周年记念日。当时只敢作easy还得抄答案的我想啥时候能作上medium啊,事到现在,感受都不是啥障碍了,这道题也已经作了第四遍了,抽出来的DFS递归模板放在这里总结一下。
这题内容就是就是给个数组,里面的数字都是独一无二的,把它全部的子集都输出出来。算法
题目中给了个例子,好比[1,2,3]
,第一次抽出1,而后在1的基础上加2,再加3。加完以后再往回返,去掉3,再去掉2,发现能够加3,造成[1,3]
,再到2,以此类推。
思路很容易,可是写的时候须要用到递归的手法,这个仍是须要点儿思惟过程的,推荐用IDE的debug功能一步一步走走看。数组
public class Subsets { public List<List<Integer>> subsets(int[] nums) { //先把输出的东西摆上去。 List<List<Integer>> result = new ArrayList<>(); //排除corner case,就是返回一空的。 if(nums == null || nums.length == 0) return result; //这个就是用来搜集每一个子集的。 List<Integer> list = new ArrayList<>(); dfs(result, list, 0, nums); return result; } public void dfs(List<List<Integer>> result, List<Integer> list, int start, int[] nums){ //先把当前的子集加上,这里添加的语法我命名为『照相法』 result.add(new ArrayList<>(list)); //注意这里要从start位置开始循环,不然就是stackoverflow for(int i = start; i < nums.length; i++){ //添加start位置的数字 list.add(nums[i]); //开始递归,好比把1加上去以后,就稳住1,找后面好比2. dfs(result, list, i+1, nums); //backtrack,就是把以前加上的去掉,至关于往回走,好比以前加到1,2,3 //就把3去掉,而后再找。 list.remove(list.size()-1); } } }
算法课讲过,这个复杂度是指数次,能实现出来就好了,无法优化。优化
这题就是模板,DFS的模板,就是一个容器来回抓,容器的容器每次把这个容器记录下来。还有递归的debug就是人工模仿IDE,一步一步走。debug
稍有不一样,就是数组里面的数字可能有重复,同时要求子集输出不能用重复的元素,一个很是典型的follow up。code
重点在于判断重复数字,把重复的状况跳过去。模板仍是同样的。排序
public class SubsetsII { public List<List<Integer>> subsetsWithDup(int[] nums) { List<List<Integer>> result = new ArrayList<>(); if(nums == null || nums.length == 0) return result; //这里就须要排序了,给之后跳太重复数字埋下伏笔 Arrays.sort(nums); //剩下都是同样的 List<Integer> list = new ArrayList<>(); dfs(result, list, 0, nums); return result; } public void dfs(List<List<Integer>> result, List<Integer> list, int start, int[] nums){ result.add(new ArrayList<>(list)); for(int i = start; i < nums.length; i++){ //关键就在这一句,每次循环起始的数字不能和以前重复。 if(i > start && nums[i] == nums[i-1]) continue; list.add(nums[i]); dfs(result, list, i+1, nums); list.remove(list.size()-1); } } }
不分析了,反正指数次。递归
这里注意用好模板,循环的时候把起始的start写成了0,直接就爆了。leetcode