寫這個系列是由於記念一下去年的今天,就是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