1. 问题描述:
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subsets
2. 思路分析:
① 题目其实比较好理解,对于这种尝试可能性的题目,使用递归就再适合不过了,逐个可能的情况进行尝试最后得到最终的答案,知道使用递归来解决剩下来的就比较好办了,因为我们很容易想到这样的思路,比如对于集合[1,2,3]我们可以这样想,对于当前的数字我可以拿取,也可以不拿取所以这就对应着两个不同的平行状态,所以使用两个方法进行递归调用,递归调用下一个位置的情况即可解决这个问题,递归的出口为数组的边界了,而到达数组边界说明一个子集已经形成,
② 这道题目的重点除了使用递归解决之外,我们还可以学习到怎么样使用数据结构来记录中间过程形成的子集,因为之前有遇到过这样的情况,之前领扣中的有一道题目也是返回List<List<Integer>>,其中使用的是栈来记录的,这里也是可以使用栈来做记录,最后可以将栈直接丢给ArrayList即可,这样的做法非常方便很值得学习
③ 除了上面的写法之外,还有另外一种写法,在for循环中进行递归,其实思路也是差不多的,也是考虑可以拿取当前的元素与不拿取当前的元素,所以在for循环中使用一个方法递归调用即可,调用递归方法之前往记录中间过程的栈加入当前元素,递归调用弹出上一次加入的元素,因为是在for循环中进行递归调用所以可以拿取元素与不拿取元素那么其实就是一种方案,所以我们在递归方法的开头就需要将stack中的元素加入到List中,因为每递归调用一次都表示的是一种方案,所以需要在递归方法一开始就加入到List结果中,这也是与上面写法的不一样的一点,其实也有点类似于递归的出口
3. 代码如下:
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
/*可以使用递归来解决: 可以拿取当前的元素也可以不拿取当前的元素*/
recursion(nums, 0, new Stack<Integer>());
return res;
}
public void recursion(int[] nums, int pos, Stack<Integer> stack) {
if (pos == nums.length) {
/*要学习一下这种将栈中的元素直接弄到ArrayList中的做法*/
res.add(new ArrayList<>(stack));
return;
}
stack.add(nums[pos]);
recursion(nums, pos + 1, stack);
stack.pop();
recursion(nums, pos + 1, stack);
}
}
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
recursion(nums, 0, new Stack<Integer>());
return res;
}
private void recursion(int[] nums, int pos, Stack<Integer> stack) {
res.add(new ArrayList<>(stack));
for(int i = pos; i < nums.length; ++i){
stack.add(nums[i]);
recursion(nums, i + 1, stack);
stack.pop();
}
}
}