回溯——子集
题目:
给你一个整数数组 nums ,数组中的元素 **互不相同 **。返回该数组所有可能的子集(幂集)。
这道题是求数组的幂集,核心思路是回溯算法(深度优先搜索),也可以用迭代法实现。因为数组元素互不相同,所以无需去重,直接生成所有子集即可。
方法一:回溯法
思路
- 子集的本质:每个元素都有选或不选两种选择,最终遍历所有组合。
- 回溯流程:
- 从数组第一个元素开始,依次决定是否加入当前子集。
- 加入元素后递归处理下一个元素。
- 递归返回后回溯(移除最后加入的元素),尝试不选该元素的情况。
- 每次递归都将当前子集加入结果集(空集也是子集)。
代码解释
- 全局变量:res存所有子集,path存当前正在构建的子集。
- 回溯函数:
res.add(new ArrayList<>(path)):必须新建列表,否则后续回溯会修改已加入的子集。
for(int i = start; i<nums.length;i++):从当前start开始遍历,避免生成重复子集(如[1,2]和[2,1])。
三步核心:选择->递归->回溯。
完整代码实现如下:
class Solution {
//存储当前结果
List<List
//存储当前子集
List
public List<List
backtrack(nums,0);
return res;
}
private void backtrack(int[] nums,int start){
//每次递归都将当前子集加入结果集(空集也会被加入)
res.add(new ArrayList<>(path));
//遍历从start开始的所有元素
for(int i = start;i<nums.length;i++){
//1. 选择:将当前元素加入子集
path.add(nums[i]);
//2. 递归:得到1的所有组合([1],[1,2],[1,2,3],[1,3])
baktrack(nums,i+1); //递归处理下一个元素,避免重复
//3. 回溯:移除每层递归最后加入的元素,元素2需要移除3和2两个元素
path.remove(path.size()-1);
}
}
}
回溯关键点解释:
元素3的for循环进入下一个元素的递归会先把当前子集加入res数组,由于i+1= nums.length,不会进入递归,返回到元素3的递归,进入回溯逻辑,把3移除。由于元素3已经到了是for循环的最后一个元素,移除3以后就直接返回到元素2的递归,把2移除,由于for循环并未遍历到末尾,执行i++,进入到元素3的for循环,将元素3加入path,,把[1,3]加入res,返回到3的递归(这里的3的递归实质是在2的递归中执行的3的递归)把3移除,再返回到元素1的递归,将元素1移除。至此把元素1的所有组合添加到res中,for循环遍历到第二个元素,开始添加2的组合子集。总结:每个for循环都是处理当前元素的所有组合,通过递归来添加下一个元素到当前元素的集合中,每层递归之前将当前子集加入res中,递归到底后回溯移除最后加入的元素。
本题默认数组中不存在重复元素,拿如果数组元素会重复呢?
如果题目改成:
给你一个整数数组 nums,数组中可能包含重复元素,返回所有可能的子集。
这时候必须去重,否则结果里会有大量重复子集。
去重核心规则
- 先排序(让重复元素挨在一起)
- 跳过重复元素:同一层、相同值、不是第一个选择 → 跳过
浙公网安备 33010602011771号