题目及测试
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.