題目及測試
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.