在问 · 算法题解

491. 非递减子序列

中等回溯回溯 + 同层去重力扣原题 ↗

题目描述

输入整数数组 nums,其中可能有重复值。

子序列是从数组中删去若干元素(也可以不删)后、保持原有顺序得到的序列。找出所有元素个数至少为 2、且从前到后不下降的子序列;相邻两项相等也符合要求。

返回这些子序列,内容相同的子序列只保留一份。答案中子序列的排列顺序不限。

示例 1
输入:nums = [4,6,7,7]
输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]

解释:共有 8 个不同的非递减子序列,两个 7 组成的 [7,7] 也算在内。

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

解释:只有两个 4 能组成非递减子序列。

数据范围

  • 1 <= nums.length <= 15
  • -100 <= nums[i] <= 100

解题思路:回溯 + 同层去重

回溯:dfs(start) 从 start 往后挑下一个元素接到 path 末尾。比 path 末尾小的不能接;同一层里已经用过的数值再接一次会得到重复的子序列,用本层的 used 集合跳过。每进入一层,若 path 长度 ≥ 2 就记录一份。不能排序,因为子序列必须保持原数组顺序。

为什么正确

path 是 nums 的一个非递减子序列,且每一层 used 记录了本层已作为“下一个元素”尝试过的数值,所以同一个子序列只会被生成一次。

复杂度分析

时间 O(2^n · n):最多 2^n 个子序列,每个复制 O(n);空间 O(n) 递归栈与 path(不计结果)。

易错点

不能像子集 II 那样先排序再跳过相邻重复——排序会破坏原顺序;去重集合必须是每层新建的,而不是全局的。

参考代码

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

class Solution:
    def findSubsequences(self, nums: List[int]) -> List[List[int]]:
        res = []
        path = []

        def dfs(start):
            if len(path) >= 2:
                res.append(path[:])
            used = set()
            for i in range(start, len(nums)):
                if path and nums[i] < path[-1]:
                    continue
                if nums[i] in used:
                    continue
                used.add(nums[i])
                path.append(nums[i])
                dfs(i + 1)
                path.pop()

        dfs(0)
        return res

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

其他解法

回溯在搜索过程中用“同一层不选相同的数”去重并剪掉递减分支;二进制枚举先生成全部子集,再用哈希集合事后去重,更暴力也更好理解。

解法 2:二进制枚举 + 哈希去重 时间 O(2ⁿ·n),空间 O(2ⁿ·n)

用 n 位二进制数枚举所有子序列,第 i 位为 1 表示选择 nums[i]。对每个子序列检查长度是否至少为 2、是否非递减。合法的子序列转成可哈希的键放入集合,重复出现的序列只保留第一次。

思路最直接,不需要考虑回溯中的去重规则。每个子集都要完整生成和检查,并要用哈希集合存下所有结果来去重,额外空间大。

class Solution:
    def findSubsequences(self, nums: List[int]) -> List[List[int]]:
        n = len(nums)
        seen = set()
        res = []
        # 用 n 位二进制数枚举所有子序列
        for mask in range(1, 1 << n):
            seq = [nums[i] for i in range(n) if mask >> i & 1]
            if len(seq) < 2:
                continue
            if any(seq[i] > seq[i + 1] for i in range(len(seq) - 1)):
                continue
            key = tuple(seq)
            # 哈希集合去重:相同的序列只保留一次
            if key not in seen:
                seen.add(key)
                res.append(seq)
        return res

回溯 专题的其他题目

查看 回溯 全部题目 →