【Hot 100】39. 组合总和

请添加图片描述

  • 🙋‍♂️ 作者:海码007
  • 📜 专栏:算法专栏
  • 💥 标题:【Hot 100】39. 组合总和
  • ❣️ 寄语:书到用时方恨少,事非经过不知难!

引言

组合总和

  • 🎈 题目链接:
  • 🎈 做题状态:

我的解题

这道题有一个要注意的地方就是,这个是组合,顺序不同的组合是一种结果,例如 [2, 3, 2] 和 [3, 2, 2] 是一种结果,所以这个在遍历每一层决策树的时候就需要注意遍历的起始位置了。因为这个遍历顺序不同的也是同一种组合,所以在遍历的时候要使用一个起始索引,同一层中,这个起始索引是依次增加的。而且由于同一个数可以使用多次,所以下一层的第一个起始索引和其父节点的起始索引是相同的。

class Solution {
private:
    void backtrack(vector<vector<int>>& res, vector<int>& path, int& sum, vector<int>& candidates, int target, int startIndex)
    {
        if (sum > target) return;
        if (sum == target)
        {
            res.emplace_back(path);
            return;
        }

        for (int i = startIndex; i < candidates.size(); ++i)
        {
            sum += candidates[i];
            path.emplace_back(candidates[i]);
            backtrack(res, path, sum, candidates, target, i);
            sum -= candidates[i];
            path.pop_back();
        }
    }

public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<vector<int>> res;
        vector<int> path;
        int sum = 0;
        backtrack(res, path, sum, candidates, target, 0);

        return res;
    }
};

代码和思路分析

代码实现得很扎实,整体逻辑清晰、思路正确,非常接近 LeetCode 题解中推荐的写法。下面从思路讲解代码优点可选优化点这几个方面来详细说明。


✅ 代码思路解析

准确抓住了这道题的关键:

  1. 组合顺序无关
    [2, 2, 3][3, 2, 2] 被视为相同组合,所以需要用 startIndex 来避免重复排列。

  2. 回溯树模型

    • 每一层代表当前选择的一个数字
    • 因为可以重复选取相同数字,所以下一层递归仍然从当前 startIndex 开始。
    • 如果不能重复,下一层应该是 i + 1
  3. 剪枝逻辑

    • 当前累计和 sum 超过 target 时立即返回,避免无效递归。
  4. 结果收集时机

    • sum == target,说明组合有效,加入结果集。

✅ 代码优点总结

优点说明
✅ 回溯逻辑正确处理了重复元素、组合去重
✅ 使用 sum 提前剪枝减少递归深度
✅ 变量命名清晰sum, path, startIndex 表意准确
✅ 使用引用传参提升性能,避免拷贝

🧠 可选优化建议(不是必须)

✅ 1. 移除 sum 变量,使用参数传值(更简洁)

现在维护了一个外部 sum,虽然效率上没问题,但逻辑上更自然的写法是通过参数值传递:

void backtrack(vector<vector<int>>& res, vector<int>& path, vector<int>& candidates, int target, int startIndex) {
    if (target < 0) return;
    if (target == 0) {
        res.emplace_back(path);
        return;
    }

    for (int i = startIndex; i < candidates.size(); ++i) {
        path.emplace_back(candidates[i]);
        backtrack(res, path, candidates, target - candidates[i], i);
        path.pop_back();
    }
}

这样不需要维护一个额外的 sum,逻辑上更聚焦于“剩余的目标值”。

➡️ 优点:变量更少、代码更纯净。


✅ 2. 提前对 candidates 排序,并加入剪枝

在实际题目中,如果 candidates 无序,你可以先排序,再剪枝:

if (candidates[i] > target) break;  // 提前终止循环

➡️ 这样能进一步减少无效递归分支(不过 LeetCode 上这题不要求排序)。


✅ 最终推荐优化版本(等效逻辑,更紧凑)

class Solution {
private:
    void backtrack(vector<vector<int>>& res, vector<int>& path, vector<int>& candidates, int target, int startIndex) {
        if (target < 0) return;
        if (target == 0) {
            res.emplace_back(path);
            return;
        }

        for (int i = startIndex; i < candidates.size(); ++i) {
            path.emplace_back(candidates[i]);
            backtrack(res, path, candidates, target - candidates[i], i);  // 可以重复选
            path.pop_back();
        }
    }

public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<vector<int>> res;
        vector<int> path;
        backtrack(res, path, candidates, target, 0);
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值