在问 · 算法题解

78. 子集

题目描述

输入整数数组 nums,其中元素各不相同。

返回 nums 的所有子集,也就是幂集,空集和整个数组本身都算。

结果里不能有重复的子集。子集之间的顺序不限。

示例 1
输入:nums = [1,2,3]
输出:[[],[1],[1,2],[1,2,3],[1,3],[2],[2,3],[3]]

解释:3 个元素共有 2^3 = 8 个子集。

示例 2
输入:nums = [0]
输出:[[],[0]]

解释:只有空集和 [0] 两个子集。

数据范围

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • nums 中的元素两两不同。

解题思路

回溯枚举每个选择或跳过。

为什么正确

start 之后的元素才可继续选择。

复杂度分析

时间 O(n·2ⁿ),额外空间 O(n)。

易错点

注意边界条件并在回溯或弹出后恢复状态。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
 def subsets(self,a):
  out=[];p=[]
  def f(s):
   out.append(p[:])
   for i in range(s,len(a)):p.append(a[i]);f(i+1);p.pop()
  f(0);return out

载入编辑器,自己跑一遍 →

其他解法

二进制枚举用整数直接对应子集,无递归、最短;回溯按“选或不选”逐层构造,能剪枝、能扩展到去重等变形题。

解法 2:二进制枚举 时间 O(n·2ⁿ),空间 O(n)

n 个元素的每个子集都可以用一个 n 位二进制数表示:第 i 位是 1 表示选了 nums[i]。从 0 枚举到 2ⁿ-1,每个数字按位取出对应元素,就得到全部子集。

没有递归,代码非常短,适合 n ≤ 20 左右。缺点是不能像回溯那样在搜索中剪枝,处理第 90 题这种有重复元素的情况也不方便。

class Solution:
    def subsets(self, nums):
        n = len(nums)
        out = []
        for mask in range(1 << n):
            out.append([nums[i] for i in range(n) if mask >> i & 1])
        return out

回溯 专题的其他题目

查看 回溯 全部题目 →