题目描述:
给你一个整数数组 nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:
输入:nums = [0]
输出:[[],[0]]
提示:
1 <= nums.length <= 10
-10 <= nums[i] <= 10
nums
中的所有元素互不相同
思路:
回溯法,一般可以解决如下几种问题:
- 组合问题:N个数里面按一定规则找出k个数的集合
- 切割问题:一个字符串按一定规则有几种切割方式
- 子集问题:一个N个数的集合里有多少符合条件的子集
- 排列问题:N个数按一定规则全排列,有几种排列方式
- 棋盘问题:N皇后,解数独等等
所以,要想到使用回溯算法!!!
如果把 子集问题、组合问题、分割问题都抽象为一棵树的话,那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!
其实子集也是一种组合问题,因为它的集合是无序的,子集{1,2} 和 子集{2,1}是一样的。
那么既然是无序,取过的元素不会重复取,写回溯算法的时候,for就要从startIndex开始,而不是从0开始!
子集问题要遍历树的所有节点,因为题目是要求返回数组所有可能的子集。那么使用回溯算法,每下一层,选取一个元素,到叶子节点就全部选完了。
以示例中nums = [1,2,3]为例把求子集抽象为树型结构,如下:
注意这棵树的特性:
如果把根节点作为第 0 层,将每个节点和根节点之间树枝上的元素作为该节点的值,那么第 n
层的所有节点就是大小为 n
的所有子集。
代码:
class Solution {
//记录要返回的结果集
List<List<Integer>> res = new ArrayList<>();
//记录回溯算法的递归路径
LinkedList<Integer> path = new LinkedList<>();
//主函数
public List<List<Integer>> subsets(int[] nums) {
backTrack(nums, 0);
return res;
}
//回溯算法核心框架
public void backTrack(int[] nums, int startIndex) {
//前序位置,每个节点都是一个子集
res.add(new LinkedList<>(path));
for (int i = startIndex; i < nums.length; i++) {
//做选择
path.addLast(nums[i]);
//回溯,startIndex控制树枝的遍历,避免产生重复的子集
backTrack(nums, i + 1);
//撤销选择
path.removeLast();
}
}
}