78.Subsets

題目連接: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     }
View Code

法二(借鑑):非遞歸,與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     }
View Code

當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     }
View Code
相關文章
相關標籤/搜索