【算法】78.子集--通俗讲解
通俗易懂讲解“子集”算法题目
一、题目是啥?一句话说清
给你一个不含重复元素的整数数组,返回所有可能的子集(包括空集和它本身)。
示例:
- 输入:nums = [1,2,3]
- 输出:[[], [1], [2], [1,2], [3], [1,3], [2,3], [1,2,3]]
二、解题核心
使用回溯法(递归)或位运算来生成所有可能的子集组合。 这就像你要打包行李,对于每件物品(数组中的每个元素),你都有两种选择:放进包里或者不放进包里。
三、关键在哪里?(3个核心点)
想理解并解决这道题,必须抓住以下三个关键点:
1. 回溯法的选择与回溯
- 是什么:对于每个元素,我们有两种选择:包含它或不包含它。
- 为什么重要:通过递归地做出这些选择,我们可以系统地探索所有可能的组合,生成所有子集。
2. 递归的终止条件
- 是什么:当我们已经处理完所有元素时,递归终止。
- 为什么重要:确保算法不会无限递归,并在正确的时间将当前子集添加到结果中。
3. 避免修改原结果集
- 是什么:在将子集添加到结果时,需要创建当前子集的副本。
- 为什么重要:如果直接添加引用,后续对当前子集的修改会影响已经添加到结果中的子集。
四、看图理解流程(通俗理解版本)
让我们用 nums = [1,2,3] 的例子来可视化回溯过程:
想象你面前有三件物品:1号、2号和3号。你要决定每件物品是否放进包里:
-
开始:包是空的 []
- 先处理1号物品:可以选择放或不放
-
第一层决策(处理1号物品):
- 选择1:不放1号 → 包还是 []
- 接着处理2号物品
- 选择不放2号 → 包还是 []
- 接着处理3号物品
- 选择不放3号 → 得到子集 []
- 选择放3号 → 得到子集 [3]
- 接着处理3号物品
- 选择放2号 → 包变成 [2]
- 接着处理3号物品
- 选择不放3号 → 得到子集 [2]
- 选择放3号 → 得到子集 [2,3]
- 接着处理3号物品
- 选择不放2号 → 包还是 []
- 接着处理2号物品
- 选择2:放1号 → 包变成 [1]
- 接着处理2号物品
- 选择不放2号 → 包还是 [1]
- 接着处理3号物品
- 选择不放3号 → 得到子集 [1]
- 选择放3号 → 得到子集 [1,3]
- 接着处理3号物品
- 选择放2号 → 包变成 [1,2]
- 接着处理3号物品
- 选择不放3号 → 得到子集 [1,2]
- 选择放3号 → 得到子集 [1,2,3]
- 接着处理3号物品
- 选择不放2号 → 包还是 [1]
- 接着处理2号物品
- 选择1:不放1号 → 包还是 []
-
结束:我们得到了所有8种可能的子集。
五、C++ 代码实现(附详细注释)
方法一:回溯法(推荐)
#include <iostream>
#include <vector>
using namespace std;class Solution {
public:vector<vector<int>> subsets(vector