LongDz 的数字文墨提案

78. 子集

3 min

题目

子集 中等

解法:回溯枚举

思路

核心思想是回溯法(DFS)枚举所有组合。对每个元素,递归选择”选”或”不选”两种分支,遍历整棵决策树。

正确性源于对决策树的完整遍历。每次递归固定一个起始位置 start,确保元素不重复使用。将每个中间状态 path 都加入结果,覆盖了所有子集可能。递归深度为 n 时,恰好生成 2^n 个子集,无遗漏无重复。

  • Step 1: 初始化结果集 ans 和当前路径 path
  • Step 2: 调用 dfs(nums, 0, path, ans) 开始递归
  • Step 3: 进入 dfs 立即将当前 path 加入 ans(记录所有访问节点)
  • Step 4: 从 start 遍历到数组末尾
  • Step 5: path.push_back(nums[i]) 选择当前元素
  • Step 6: dfs(nums, i+1, path, ans) 递归处理后续元素
  • Step 7: path.pop_back() 回溯,撤销选择
  • Step 8: 返回 ans

复杂度

  • 时间复杂度: O(n×2n)O(n \times 2^n)
  • 空间复杂度: O(n)O(n)

代码

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> ans;
        vector<int> path;
        dfs(nums, 0, path, ans);
        return ans;
    }

    void dfs(const vector<int>& nums, int start, vector<int>& path, vector<vector<int>>& ans) {
        // 1. 任何时候进入都把当前 path 加入结果
        ans.push_back(path);

        // 2. 尝试从 start 位置开始向后选择元素
        for (int i = start; i < nums.size(); i++) {
            path.push_back(nums[i]);      // 选择
            dfs(nums, i + 1, path, ans);  // 递归
            path.pop_back();              // 回溯
        }
    }
};