题目描述

:::fold-green 相关原题

LeetCode 原题: ✅ 78. 子集

:::

给你一个整数数组 nums 和一个整数 target,返回所有元素和等于 target 的非空子序列。

子序列可以不连续,但必须保持原下标的相对顺序。不同的下标选择分别计入结果,即使得到的数值序列相同,也需要分别保留。结果顺序不限。

示例 1:

输入: nums = [5,5,10,2,3], target = 15
输出: [[5,10],[5,10],[10,2,3],[5,5,2,3]]
解释: 两个 [5,10] 使用了不同位置的 5;[5,5,2,3] 跳过 10,因此是子序列而非连续子数组。

示例 2:

输入: nums = [0,0], target = 0
输出: [[0],[0],[0,0]]
解释: 按下标区分三个非空选择,空集不输出。

提示:

  • 元素允许为负数。
  • 和使用 64 位整数。
  • 结果顺序不限。
  • 输出数量最坏为指数级,适合能够完整枚举答案的输入规模。

题意分析

子序列只要求下标递增,不要求连续,也不能重排。相同数值来自不同下标时仍是不同方案,因此每个下标都要独立决定选或不选,不能按数值去重。

解法:按下标选与不选的回溯

核心思路

[!blue]

dfs(index,sum) 表示前 index 个位置已经决定完毕,path 保存所选值且顺序与原数组一致,sum 是它们的 64 位累计和。当前下标先走不选分支,再将该值加入路径走选择分支;返回后移除末项,恢复调用前状态。

每一组下标选择都对应唯一一条分支路径,全部递归到数组末尾后,筛选路径非空且 sum == target 的叶子。保存时复制路径,避免后续回溯修改已经收集的结果。

元素可以为负数或 0,所以当前和超过目标仍可能被后续负数抵消,当前和达到目标也可能继续产生其他方案。不能据此提前结束,也不能把 target == 0 时的空路径作为答案。

解题步骤

  1. 对每个下标分别递归不选与选择两种分支。
  2. 到数组末尾才根据路径非空和目标和判定保存副本。
  3. 选择分支返回后移除最后一个元素,恢复路径。

代码实现

class Solution {
    public List<List<Integer>> sumSubsequences(int[] nums, long target) {
        List<List<Integer>> out = new ArrayList<>();

        dfs(nums, 0, 0, target, new ArrayList<>(), out);

        return out;
    }

    private void dfs(
            int[] nums,
            int index,
            long sum,
            long target,
            List<Integer> path,
            List<List<Integer>> out) {
        if (index == nums.length) {
            if (!path.isEmpty() && sum == target) {
                out.add(new ArrayList<>(path));
            }

            return;
        }

        dfs(nums, index + 1, sum, target, path, out);
        path.add(nums[index]);
        dfs(nums, index + 1, sum + nums[index], target, path, out);
        path.remove(path.size() - 1);
    }
}
func sumSubsequences(nums []int, target int64) [][]int {
    out := [][]int{}
    path := []int{}
    var dfs func(int, int64)
    dfs = func(index int, sum int64) {
        if index == len(nums) {
            if len(path) > 0 && sum == target {
                out = append(out, append([]int(nil), path...))
            }
            return
        }
        dfs(index+1, sum)
        path = append(path, nums[index])
        dfs(index+1, sum+int64(nums[index]))
        path = path[:len(path)-1]
    }
    dfs(0, 0)
    return out
}

复杂度分析

  • 时间复杂度:$O(2^n+R)$。
  • 空间复杂度:辅助空间 $O(n)$,结果空间 $O(R)$;R 为全部输出序列的元素总数。

关键点总结

[!green]

元素可负可零,当前和超过目标不能剪枝,提前等于目标也不能停止,否则会漏掉后续补零或抵消的方案。

易错点总结

[!yellow]

不能排序输入或用集合去重,否则会改变原顺序或丢失不同下标的方案。

相似题目

题目 难度 关联与区别
78. 子集 中等 同样按下标选或不选,本题仅输出目标和非空子序列,并保持原下标顺序。
39. 组合总和 中等 同样枚举目标和方案,原题正整数可重复使用,本题每个下标只能用一次且可能有负数,不能照搬剪枝。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/863935607
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!