题目:给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
//解题方法1:迭代思想:首先我们把数组中的每一位是否可以用到通过二进制表示,用到为1,用不到为0,然后求出数组长度n,则可以得到一共有2^n个子集,空集其实就是0000……表示,然后因为不知道每次输入的数组的长度,所以可以用链表来表示
// class Solution {
// //用于存放每一个子集
// List<Integer> t = new ArrayList<Integer>();
// //把每一个子集放如到链表中
// List<List<Integer>> ans = new ArrayList<List<Integer>>();
public List<List<Integer>> subsets(int[] nums) {
//求数组长度
int n = nums.length;
//要求出2^n个子集,因此我们要循环2^n次,i的值其实就是对应着数组中的每一位,然后我们根据i进行“位”的操作,来输出每一位
for(int i=0 ; i<(1<<n) ; i++){
//每次循环我们都要把t清空
t.clear();
//然后我们再根据i进行“位比较”,因为我们把i换算成二进制数,然后把每一位是1的都对应着添加到t中
for(int j=0 ; j<n ; j++){
//1<<j是为了和二进制的每一位进行与逻辑,比如1<0=1就是……00001,1<1=2就是……00010
if((i&(1<<j))!=0){
t.add(nums[j]);
}
}
ans.add(new ArrayList<Integer>(t));
}
return ans;
}
}
//解题思路2:dfs(cur,n) 参数表示当前位置是 \textit{cur}cur,原序列总长度为 nn。原序列的每个位置在答案序列中的状态有被选中和不被选中两种,我们用 tt 数组存放已经被选出的数字。在进入 \text{dfs}(\textit{cur}, n)dfs(cur,n) 之前 [0, \textit{cur} - 1][0,cur−1] 位置的状态是确定的,而 [\textit{cur}, n - 1][cur,n−1] 内位置的状态是不确定的,\text{dfs}(\textit{cur}, n)dfs(cur,n) 需要确定 \textit{cur}cur 位置的状态,然后求解子问题 {\text{dfs}(cur + 1}, n)dfs(cur+1,n)。对于 \textit{cur}cur 位置,我们需要考虑 a[\textit{cur}]a[cur] 取或者不取,如果取,我们需要把 a[\textit{cur}]a[cur] 放入一个临时的答案数组中(即上面代码中的 tt),再执行 {\text{dfs}(cur + 1}, n)dfs(cur+1,n),执行结束后需要对 tt 进行回溯;如果不取,则直接执行 {\text{dfs}(cur + 1}, n)dfs(cur+1,n)。在整个递归调用的过程中,\textit{cur}cur 是从小到大递增的,当 \textit{cur}cur 增加到 nn 的时候,记录答案并终止递归。可以看出二进制枚举的时间复杂度是 O(2 ^ n)。
class Solution {
//用于存放每一个子集
List<Integer> t = new ArrayList<Integer>();
//把每一个子集放如到链表中
List<List<Integer>> ans = new ArrayList<List<Integer>>();
public List<List<Integer>> subsets(int[] nums) {
//我们从第一位开始,其实这递归的逆用,从最大子集找递归到最小子集
dfs(0,nums);
return ans;
}
public void dfs(int cur,int[] nums){
if(cur==nums.length){
ans.add(new ArrayList<Integer>(t));
return;
}
//表示包含当前位,进行递归
t.add(nums[cur]);
dfs(cur+1,nums);
//保是不包含当前位,进行递归
t.remove(t.size()-1);
dfs(cur+1,nums);
}
}