LeetCode 78. 子集 | C++ 回溯算法(选或不选)题解
📌 题目描述
题目级别:中等
给你一个整数数组 nums ,数组中的元素互不相同。返回该数组所有可能的子集(幂集)。
解集不能 包含重复的子集。你可以按任意顺序返回解集。
- 示例 :
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
💡 解题思路:回溯算法(选或不选视角)
求一个集合的子集,本质上是在回答一个问题:对于集合中的每一个元素,我是把它放进子集里,还是不放进去?
这就形成了一棵非常规整的满二叉树 决策过程。假设数组长度为 NNN,我们需要做 NNN 次决策。
以 nums = [1, 2, 3] 为例:
- 面对
1:选它,或者不选它。(2 种状态) - 面对
2:选它,或者不选它。(2 种状态) - 面对
3:选它,或者不选它。(2 种状态)
当我们对所有元素都做完决定(即递归深度达到 NNN),我们就得到了一个确定的子集。把所有的叶子节点收集起来,就是完整的幂集!
核心代码逻辑拆解
- 不选当前元素 :直接进入下一层递归
dfs(nums, u + 1);。此时tmp数组保持原样。 - 选当前元素 :先把它加进来
tmp.push_back(nums[u]);,然后再进入下一层递归dfs(nums, u + 1);。 - 撤销选择(回溯) :从下一层递归回来后,必须把刚才加进来的元素踢出去
tmp.pop_back();,恢复现场,以便去尝试其他的分支。
💻 C++ 代码实现
cpp
class Solution {
public:
int n;
vector<vector<int>> res; // 存放所有的子集
vector<int> tmp; // 存放当前正在构建的子集
vector<vector<int>> subsets(vector<int>& nums) {
dfs(nums, 0);
return res;
}
// u 表示当前正在对 nums[u] 做"选或不选"的决策
void dfs(vector<int>& nums, int u) {
// 1. 递归终止条件:已经对所有元素做完了决策
if (u == nums.size()) {
res.push_back(tmp); // 将当前构建好的子集加入结果集
return;
}
// 2. 决策分支一:【不选】当前的数字 nums[u]
// 直接跳过当前数字,去考察下一个数字
dfs(nums, u + 1);
// 3. 决策分支二:【选】当前的数字 nums[u]
tmp.push_back(nums[u]); // 记录选择
dfs(nums, u + 1); // 带着这个选择去考察下一个数字
// 4. 回溯:撤销选择,恢复现场
tmp.pop_back();
}
};