NG SWE · Phone Screen Coding

Bloomberg Subsets:回溯枚举与重复值去重

给数组返回所有子集;说明空集、重复元素时的同层跳过、指数输出下界与递归回退。

题目重点:给数组返回所有子集;说明空集、重复元素时的同层跳过、指数输出下界与递归回退。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 训练版输入可能含重复数值,返回按元素多重集区分的唯一子集;每个子集内部升序,整体 DFS 顺序。
  • 若题目把相同值但不同下标当不同子集,则不应去重,输出可达 2^n。

解题路线

  1. 先排序,让相同值相邻;每个递归调用先记录当前 path 作为一个子集,包括根的空集。
  2. 从 start 起枚举下个元素,若 i>start 且 a[i]==a[i−1],跳过同一层的重复首选;进入下一层后允许再选相同值。
  3. 回溯后 pop,确保分支之间互不污染;用 [1,2,2] 手推 6 个不同子集。
  4. 解释输出本身可能指数级,不存在把全部结果写出仍为 O(n) 的算法。

手动走一遍

[1,2,2] 的唯一子集是 [],[1],[1,2],[1,2,2],[2],[2,2];第二个 2 在根层被跳过,但在第一个 2 之后可以选入。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

def distinct_subsets(values):
    """Return distinct subsets of a multiset, including the empty subset."""
    ordered = sorted(values)
    result = []
    current = []

    def visit(start):
        result.append(current.copy())
        for i in range(start, len(ordered)):
            if i > start and ordered[i] == ordered[i - 1]:
                continue
            current.append(ordered[i])
            visit(i + 1)
            current.pop()

    visit(0)
    return result

复杂度

排序 O(n log n);最多 2^n 个子集,逐项复制路径的总时间/输出空间 O(n·2^n),递归额外 O(n)。

易错点与追问

容易失分的边界

  • 全局跳过重复值导致 [2,2] 消失。
  • 记录同一个可变 path 引用而非复制。
  • 忘记空集。
  • 把指数输出成本说成线性。

继续深挖

  • 若只需子集数?
  • 如何惰性生成避免一次存全部?
  • 若要固定大小 k 的子集怎么剪枝?
✓已复制微信号!