题目描述:
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
分析:
这个题的题意很明确就是求一组数的所有子集;我们利用数学知识很容易能求出它的子集一共多少种。
求所有子集的数量:
我们高中已经有了排列组合的知识了,所以求所有子集的数量的思想就是从所有的数字中选取任意不重复的组合,我们再思考一下,其实每个数字就两种可能:要么选要,要么不选。
---------> 因此,所有子集的数量就等于每个数组的选取可能(2种)*总的数字个数,即:n^2*n种
。
解题思路:
在上面分析的过程中我们已经知道了子集有多少种,那么现在就需要将这些子集一一列举出来。对于找出所有的组合这种情况采用暴力是非常麻烦并且不合理的,我们采用递归和回溯来解决该问题。
在此,我先将递归代码给出,然后再结合代码一步一步分析。
代码:
class Solution {
private List<List<Integer>> list=new ArrayList<>();
private List<Integer> temp=new ArrayList<>();
//dfs
public void dfs(int cur,int[] nums)
{
//递归出口:如果cur==nums.length就结束
if(cur==nums.length)
{
list.add(new ArrayList<>(temp));
return;
}
//选择当前元素:需要先存储在temp中,在回溯的时候来选择要不要此元素
temp.add(nums[cur]);
dfs(cur+1,nums);
//不选择当前元素(首先需要将临时list中的元素删除掉))
temp.remove(temp.size()-1);
dfs(cur+1,nums);
}
public List<List<Integer>> subsets(int[] nums) {
dfs(0,nums);
return list;
}
}
变量说明:
- list来作为最后返回所有子集的集合;
- temp来临时存放所有可能被选择的元素,主要为后面递归过程中是否选择该元素做准备。
- dfs是来实现递归和回溯的函数;参数cur表示当前元素位于数组的下标;参数nums表示遍历的数组。
递归整体思路:
对于每个值都有取和不取两个状态,在递归的代码中首先将元素添加到temp中,然后获取取该元素的情况,再将该元素删除就得到不取该元素的情况。
递归执行过程:
首先在subsets函数中调用递归函数,下标从0开始,见下图所示:
上图将dfs(2,nums)详细过程已经写出,递归的过程比较繁琐和难以理解,好好理理还是可以理清楚的。