leetcode-78-子集(subsets)-java
題目及測試
package pid078; import java.util.List; /*子集 給定一組不含重複元素的整數陣列 nums,返回該陣列所有可能的子集(冪集)。 說明:解集不能包含重複的子集。 示例: 輸入: nums = [1,2,3] 輸出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ] */ public class main { public static void main(String[] args) { int[] testTable = {1,2,3}; test(testTable); } private static void test(int[] ito) { Solution solution = new Solution(); long begin = System.currentTimeMillis(); System.out.println("ito= "); for (int i = 0; i < ito.length; i++) { System.out.print(ito[i]+" "); }//開始時列印陣列 System.out.println(); List<List<Integer>> rtn=solution.subsets(ito);//執行程式 long end = System.currentTimeMillis(); for (int i = 0; i < rtn.size(); i++) { for(int j=0;j<rtn.get(i).size();j++){ System.out.print(rtn.get(i).get(j)+" "); } System.out.println(); }//列印結果幾陣列 System.out.println(); System.out.println("耗時:" + (end - begin) + "ms"); System.out.println("-------------------"); } }
自己沒想出來
解法1(別人的)
不錯的方法
https://blog.csdn.net/wodedipang_/article/details/52996928
使用位操作,不使用遞迴。首先,計算一下該陣列nums一共有多少個子集,設陣列nums的長度為n,那麼它的子集總數為num=2^n。
設定一個變數index,其初始值為1。那麼從0到2^n-1中數,對於每一個數i,用index(從1到10,100,100(2進位制))與這個i進行與操作,如果得出的結果大於0,則把該數輸入到List<>中取,比較n次,因為陣列的長度為n。
public List<List<Integer>> subsets(int []nums) { List<List<Integer>> list=new ArrayList<List<Integer>>(); if(nums==null||nums.length==0) { return list; } int n=nums.length; //陣列的長度 int num=(int)Math.pow(2,n); for(int i=0;i<num;i++) //這裡是2^n次的 { int index=1; List<Integer> temp=new ArrayList<Integer>(); for(int j=0;j<n;j++) //陣列的長度 { int data=i&index; //一共要計算num*n次的位與操作 System.out.println("data="+data); if(data>0)//選取data大於0的數,說明該數還沒有被選擇過的。 { temp.add(nums[j]); } index=index<<1;//左乘2的1次方 } list.add(temp); } return list; }
解法2(別人的)
回溯演算法
這道題需要求給定陣列的子集,特別要求有:
1、必須是升序
2、不能出現重複的
所以做法其實也就是,首先排序,然後回溯。。和昨天那題一樣,可以回去看一下。記得選擇下一個的時候,別和當前的值重複就可以了。
public class Solution { /** * 原來這道題是不在乎順序的。。我用的方式是我習慣的。。沒亮點,所以用List了。。 * 用bit位可也咯,用boolean陣列代替也好。。。都可以。。看你習慣哪種了,反正沒新增一個,都要遍歷一次,心累 * * 對了,List是引用。。所以要重新建立一個新的物件才行哦。。。不然就掛了。。 * */ int[] nums; List<List<Integer>> result; public void find(int index,List<Integer> last){ if(index>=nums.length) return ; ArrayList<Integer> item=new ArrayList<Integer>(); item.addAll(last); item.add(nums[index]); result.add(item); find(index+1,last); find(index+1,item); } public List<List<Integer>> subsets(int[] nums) { Arrays.sort(nums); this.nums=nums; this.result=new ArrayList<List<Integer>>(); int i=0; ArrayList<Integer> tmp=new ArrayList<Integer>(); result.add(tmp); find(i,tmp); return result; } }
解法3(別人的)
回溯演算法|遞迴實現
本解法採用回溯演算法實現,回溯演算法的基本形式是“遞迴+迴圈”,正因為迴圈中巢狀著遞迴,遞迴中包含迴圈,這才使得回溯比一般的遞迴和單純的迴圈更難理解,其實我們熟悉了它的基本形式,就會覺得這樣的演算法難度也不是很大。原陣列中的每個元素有兩種狀態:存在和不存在。
① 外層迴圈逐一往中間集合 temp 中加入元素 nums[i],使這個元素處於存在狀態
② 開始遞迴,遞迴中攜帶加入新元素的 temp,並且下一次迴圈的起始是 i 元素的下一個,因而遞迴中更新 i 值為 i + 1
③ 將這個從中間集合 temp 中移除,使該元素處於不存在狀態
public class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
List<Integer> temp = new ArrayList<Integer>();
dfs(res, temp, nums, 0);
return res;
}
private void dfs(List<List<Integer>> res, List<Integer> temp, int[] nums, int j) {
res.add(new ArrayList<Integer>(temp));
for(int i = j; i < nums.length; i++) {
temp.add(nums[i]); //① 加入 nums[i]
dfs(res, temp, nums, i + 1); //② 遞迴
temp.remove(temp.size() - 1); //③ 移除 nums[i]
}
}
}
10 / 10 test cases passed. Runtime: 2 ms Your runtime beats 61.73% of javasubmissions.
解法4(別人的)
組合|非遞迴實現
這種方法是一種組合的方式
① 最外層迴圈逐一從 nums 陣列中取出每個元素 num
② 內層迴圈從原來的結果集中取出每個中間結果集,並向每個中間結果集中新增該 num 元素
③往每個中間結果集中加入 num
④將新的中間結果集加入結果集中
public class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
res.add(new ArrayList<Integer>());
for (int num : nums) { // ①從陣列中取出每個元素
int size = res.size();
for (int i = 0; i < size; i++) {
List<Integer> temp = new ArrayList<>(res.get(i)); // ②逐一取出中間結果集
temp.add(num); // ③將 num 放入中間結果集
res.add(temp); // ④加入到結果集中
}
}
return res;
}
}
10 / 10 test cases passed. Runtime: 2 ms Your runtime beats 61.73% of javasubmissions.