幂集。编写一种方法,返回某集合的所有子集。集合中不包含重复的元素。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
分析:
方法1:DFS+回溯
拿 [1,2,3] 举例,寻找它的子集过程可以看做:
- [ ] 空集开始
- [1] 添加 1
- [1,2] 添加 2
- [1,2,3] 添加 3,遍历到头
- [1,3] 回溯到 [1],添加 3,遍历到头
- [2] 回溯到 [ ],添加 2
- [2,3] 添加 3,遍历到头
- [3] 回溯到 [ ],添加 3
我们可以用深度遍历(DFS)来模拟这个过程,递归参数为数组索引,递归终止条件就是遍历到头,每一次递归我们就添加一次子集,子集添加当前元素,回溯时,就将当前元素删除。
时间复杂度:O(2^n) 假设数组是个二进制数,里面的数字只有选中和未选中状态即 1 和 0,那么它的子集个数就是这个二进制数,即 2^n
空间复杂度:O(2^n)
class Solution {
//结果集合
List<List<Integer>> res = new ArrayList<>();
//子集
List<Integer> list = new ArrayList<>();
//数组
int[] nums;
public List<List<Integer>> subsets(int[] nums) {
this.nums = nums;
dfs(0);
return res;
}
public void dfs(int n){
//添加子集
res.add(new ArrayList(list));
//遍历
for(int i = n; i < nums.length; ++i){
//添加该元素
list.add(nums[i]);
//深度遍历
dfs(i+1);
//删除该元素
list.remove(list.size()-1);
}
}
}
方法2:动态规划
依然拿 [1,2,3] 举例,寻找它的子集过程可以看做:
- [ ] 没有元素
- [ ] [1] 添加第一个元素
- [ ] [1] [2] [1,2] 添加第二个元素
- [ ] [1] [2] [1,2] [3] [1,3] [2,3] [1,2,3] 添加第三个元素
每一次添加就相当于在前面子集的情况下添加自己的元素,转移方程不好表示,就不写了。
时间复杂度:O(2^n)
空间复杂度:O(2^n)
class Solution {
//结果集合
List<List<Integer>> res = new ArrayList<>();
//数组
int[] nums;
public List<List<Integer>> subsets(int[] nums) {
this.nums = nums;
//添加空集
res.add(new ArrayList<>());
recur(0);
return res;
}
public void recur(int n){
//到头
if(n == nums.length){
return;
}
//结果集合大小
int size = res.size();
//遍历,添加元素
for(int i = 0; i < size; ++i){
//复制子集
List<Integer> list = new ArrayList(res.get(i));
//添加元素
list.add(nums[n]);
//添加到结果集
res.add(list);
}
//递归
recur(n+1);
}
}
题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/power-set-lcci