題目連接:https://leetcode.com/problems/subsets/description/數組
題目大意:給出一個數組序列的全部子集合,包括空集。(數組中的數字惟一)ide
法一(借鑑):利用77題組合的思想,只是這裏不須要判斷集合中總數是否是達到k值,而是將每一次獲得的集合都加入結果集中。代碼以下(耗時5ms):函數
1 public List<List<Integer>> subsets(int[] nums) { 2 List<List<Integer>> list = new ArrayList<List<Integer>>(); 3 List<Integer> tmp = new ArrayList<Integer>(); 4 dfs(list, tmp, nums, 0); 5 return list; 6 } 7 public static void dfs(List<List<Integer>> list, List<Integer> tmp, int[] nums, int start) { 8 list.add(new ArrayList<Integer>(tmp)); 9 for(int i = start; i < nums.length; i++) { 10 tmp.add(nums[i]); 11 dfs(list, tmp, nums, i + 1); 12 tmp.remove(tmp.size() - 1); 13 } 14 }
法二(借鑑):非遞歸,與46題的法二的非遞歸方法很相似,只是這裏少了內層的for循環代碼,由於是組合,因此不須要考慮當前下標以前的數。代碼以下(耗時3ms):spa
1 public List<List<Integer>> subsets(int[] nums) { 2 List<List<Integer>> res = new ArrayList<List<Integer>>(); 3 res.add(new ArrayList<Integer>());//加入空集 4 for(int num : nums) {//遍歷nums數組 5 List<List<Integer>> tmp = new ArrayList<List<Integer>>();//臨時結果集 6 for(List<Integer> r : res) {//遍歷結果集 7 //新建一個對象a指向r地址空間,在執行完a.add()以後,a的地址空間發生變化,獲得一個新的地址空間,而res結果集中的原地址空間中的數據仍保持不變,保證了一致性,不會篡改數據 8 // System.out.println("r:" + r.hashCode()); 9 List<Integer> a = new ArrayList<Integer>(r); 10 // System.out.println("a:" + a.hashCode()); 11 a.add(num); 12 // System.out.println(":" + a.hashCode()); 13 tmp.add(a);//將新獲得的結果放入臨時結果集 14 15 //雖然你看上去上面的代碼新建了一個變量,使得代碼更加繁瑣,可是實際上是有必要並且必須的 16 //下面的這段代碼不可行,由於r所拿到的實際上是res結果集裏面的某一個list的地址,並且並無新開闢空間,也就沒有更換地址 17 //這樣在執行r.add()函數的時候,加入的num值就是在原地址空間的基礎上加的,可是在執行完add()函數以後,r的地址會變成一個新的地址,至於爲何,看了源碼也無從得知 18 //而因爲在r.add()以後r的地址已經變了,下面new一個新對象,指向變後的地址空間,讓其存留,可是此時其實res結果集中的數據也已經變了,由於r是在原地址上作的操做 19 //當再次執行下面的res.addAll()的操做時,會獲得不少個地址空間相同的對象,由於其實一直都指向一個地址空間,也就是r的地址空間 20 // r.add(num); 21 //new的做用僅僅是將原始數據存留,而不是新開闢一個地址空間,也就是新建一個對象指向r地址空間 22 // tmp.add(new ArrayList<Integer>(r)); 23 } 24 //addAll()是在res原結果集的基礎上將tmp整個的加入res結果集中,而不是用tmp將res覆蓋 25 res.addAll(tmp);//將臨時結果集賦給res結果集 26 27 } 28 return res; 29 }
當nums={1,2,3}時,運行結果以下:.net
[1]--->第一次外層for循環 list:[[], [1]] [2]--->第二次外層for循環 [1, 2] list:[[], [1], [2], [1, 2]] [3]--->第三次外層for循環 [1, 3] [2, 3] [1, 2, 3] list:[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]] [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
法三(借鑑):位運算,還不是很懂,參考http://m.blog.csdn.net/camellhf/article/details/73551410。代碼以下(耗時3ms):code
1 public List<List<Integer>> subsets(int[] nums) { 2 List<List<Integer>> res = new ArrayList<List<Integer>>(); 3 int length = nums.length;//記錄數組個數 4 int num = (int) Math.pow(2, length);//記錄子集個數 5 for(int i = 0; i < num; i++) {//初始化結果集,必須的,若是不初始化下面res.get()的時候會出錯 6 res.add(new ArrayList<Integer>()); 7 } 8 for(int i = 0; i < nums.length; i++) { 9 for(int j = 0; j < num; j++) { 10 //System.out.println(j + "," + (j>>i)); 11 if(((j >> i) & 1) == 0) { 12 res.get(j).add(nums[i]); 13 } 14 } 15 } 16 return res; 17 }