day156—回溯—组合总和(LeetCode-216)

题目描述

找出所有相加之和为 nk个数的组合,且满足下列条件:

  • 只使用数字1到9
  • 每个数字 最多使用一次

返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

示例 1:

复制代码
输入: k = 3, n = 7
输出: [[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。

示例 2:

复制代码
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
解释:
1 + 2 + 6 = 9
1 + 3 + 5 = 9
2 + 3 + 4 = 9
没有其他符合的组合了。

示例 3:

复制代码
输入: k = 4, n = 1
输出: []
解释: 不存在有效的组合。
在[1,9]范围内使用4个不同的数字,我们可以得到的最小和是1+2+3+4 = 10,因为10 > 1,没有有效的组合。

提示:

  • 2 <= k <= 9
  • 1 <= n <= 60

解决方案:

这段代码的核心功能是找出所有由 k 个不同的 1~9 数字组成、和为 n 的组合(combinationSum3 问题),采用「类成员变量 + 倒序枚举 + 精准剪枝」的回溯思路实现,既保留了你习惯的代码风格,又复用了高效的剪枝逻辑,是该问题的最优解法之一。

核心逻辑

  1. 成员变量作用

    • path:临时数组,存储当前正在构造的数字组合(比如选了 9、2 后,path 为 [9,2]);
    • ans:最终结果数组,存储所有符合 "k 个数、和为 n、1~9 不重复" 的组合。
  2. 递归函数 dfs 逻辑

    • 参数说明:
      • i:当前可选数字的上界(只能从 1~i 中选数,初始为 9,符合 1~9 的范围限制);
      • k:需要选取的数字总个数(全程不变);
      • n:剩余需要凑的和(每选一个数 j,就减去 j)。
    • 核心剪枝(提前终止无效递归):if (n < 0 || n > (i * 2 - d + 1) * d / 2)
      • n < 0:剩余和为负,不可能凑出合法组合;
      • n > (i*2 -d +1)*d/2:剩余 d 个数的最大可能和(从 i 倒着选 d 个不同数的和,如 i=9、d=2 时最大和 = 9+8=17),超过则直接剪枝,避免无效枚举。
    • 终止条件:if (k == path.size()):当前组合的长度等于 k,说明已选够 k 个数(剪枝已保证剩余和为 0),将path加入ans后返回。
    • 核心流程(倒序枚举 + 回溯):① 遍历从id的所有数字 j(倒序选数避免重复组合,j≥d 保证能凑够 d 个数,比如 d=2 时 j 最小为 2,因为 2+1=3 能凑 2 个数);② 选数字 j:将 j 加入path,递归调用dfs(j-1, k, n-j)(下一轮只能选更小的数,保证数字不重复);③ 回溯:递归返回后执行path.pop_back(),删掉刚加入的数字,尝试下一个可选数字。
  3. 主函数 combinationSum3

    • 从数字上界 9 开始调用dfs(初始可选数字为 1~9),启动组合构造过程;
    • 最终返回存储所有合法组合的ans

关键特点

  • 高效剪枝:通过 "剩余和的最大 / 最小范围" 判断,大幅减少递归次数,效率远高于无剪枝的暴力枚举;
  • 天然去重:倒序枚举 + 下一轮选更小的数,保证组合内数字递减(如 [9,2] 而非 [2,9]),避免重复组合(组合不考虑顺序);
  • 范围合规:初始 i=9,限制了数字只能是 1~9,符合题目要求。

总结

  1. 核心思路:递归倒序枚举 1~9 的数字,通过精准剪枝提前终止无效递归,回溯遍历所有 "k 个数、和为 n" 的合法组合;
  2. 关键操作:剪枝公式是效率核心,倒序枚举 + j≥d 是去重和避免无效枚举的关键,push_back/pop_back实现回溯;
  3. 功能效果:能输出所有符合条件的组合,无重复、无遗漏,且时间效率最优。

以输入k=3, n=9为例,最终会生成[[6,2,1],[5,3,1],[4,3,2]](倒序枚举生成的组合为降序,和正序结果等价),完全符合题目要求。

函数源码:

cpp 复制代码
class Solution {
public:
    vector<int>path={};
    vector<vector<int>>ans={};
    
    void dfs(int i, int k, int n) {
        int d = k - path.size(); // 还需要选d个数
       
        if (n < 0 || n > (i * 2 - d + 1) * d / 2) {
            return;
        }
        // 终止条件:选够k个数(d=0),且剩余和为0(隐含在剪枝里)
        if (k == path.size()) {
            ans.emplace_back(path);
            return;
        }
        // 从i倒着枚举到d(保证有足够数可选)
        for (int j = i; j >= d; j--) {
            path.push_back(j);          // 选当前数j
            dfs(j - 1, k, n - j);       // 下一轮从j-1开始选,剩余和减j
            path.pop_back();        
        }
    }

    vector<vector<int>> combinationSum3(int k, int n) {
        dfs(9, k, n);
        return ans;
    }

};
相关推荐
小O的算法实验室17 小时前
2026年ASOC,基于深度强化学习的无人机三维复杂环境分层自适应导航规划方法,深度解析+性能实测
算法·无人机·论文复现·智能算法·智能算法改进
郭涤生18 小时前
STL vector 扩容机制与自定义内存分配器设计分析
c++·算法
༾冬瓜大侠༿18 小时前
vector
c语言·开发语言·数据结构·c++·算法
Ricky111zzz18 小时前
leetcode学python记录1
python·算法·leetcode·职场和发展
汀、人工智能18 小时前
[特殊字符] 第58课:两个正序数组的中位数
数据结构·算法·数据库架构··数据流·两个正序数组的中位数
liu****18 小时前
第16届省赛蓝桥杯大赛C/C++大学B组(京津冀)
开发语言·数据结构·c++·算法·蓝桥杯
汀、人工智能18 小时前
[特殊字符] 第79课:分割等和子集
数据结构·算法·数据库架构·位运算·哈希表·分割等和子集
汀、人工智能18 小时前
[特殊字符] 第74课:完全平方数
数据结构·算法·数据库架构·图论·bfs·完全平方数
CoderCodingNo18 小时前
【GESP】C++四、五级练习题 luogu-P1177 【模板】排序
数据结构·c++·算法
Proxy_ZZ018 小时前
从零实现LDPC比特翻转译码器:C语言实战与底层逻辑解析
c语言·算法