LeetCode - 78. 子集(迭代、递归、回溯)
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
题目:
方法一: 递归
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null) return res;
helper(res,nums,new ArrayList<Integer>(),0);
return res;
}
private void helper(List<List<Integer>> res, int[] nums, ArrayList<Integer> list, int index) {
//终止条件
if (index == nums.length){
res.add(new ArrayList<Integer>(list));
return;
}
// 下到下一层
helper(res,nums,list,index+1); //list存储的是中间结果
list.add(nums[index]);
helper(res,nums,list,index+1);
//释放资源
list.remove(list.size()-1);
}
}
方法二: 迭代
先加入一个空集让他成为新的子集,然后每遍历一个元素就在原来的子集的后面追加这个值
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;
for (int mask = 0; mask < (1 << n); mask++) {
t.clear();
for (int i = 0; i < n; i++) {
if ((mask & (1<<n)) != 0){
t.add(nums[i]);
}
}
ans.add(new ArrayList<Integer>(t));
}
return ans;
}
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>(1 << nums.length);
//先添加一个空的集合
res.add(new ArrayList<>());
for (int num : nums) {
//每遍历一个元素就在之前子集中的每个集合追加这个元素,让他变成新的子集
for (int i = 0, j = res.size(); i < j; i++) {
//遍历之前的子集,重新封装成一个新的子集
List<Integer> list = new ArrayList<>(res.get(i));
//然后在新的子集后面追加这个元素
list.add(num);
//把这个新的子集添加到集合中
res.add(list);
}
}
return res;
}
类似题目:
这类题目都是同一类型的,用回溯算法 !
其实回溯算法关键在于:不合适就退回上一步
然后通过约束条件, 减少时间复杂度.
回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
…
…