组合型回溯 + 剪枝:从子集枚举到条件收敛

组合型回溯 + 剪枝

一句话总结
组合型回溯本质仍是子集型回溯,但通过“组合约束 + 单调性剪枝”,将指数级搜索空间大幅收缩,只遍历“有可能成为答案”的分支。


一、什么是组合型回溯?

在回溯问题中,我们通常会遇到两类经典模型:

  • 子集型回溯
    枚举所有可能的子集(选 / 不选),不关心长度或数值约束
  • 组合型回溯
    在子集枚举的基础上,只保留满足条件的组合

常见的组合约束包括:

  • 固定长度(如选 k 个数)
  • 固定和(如和为 target)
  • 结构合法性(如括号匹配、IP 段合法)

因此,组合型回溯 = 子集型回溯 + 条件约束 + 剪枝


二、组合型回溯的核心思想

组合型回溯的关键并不在「怎么枚举」,而在于:

当前状态已经不可能构成合法解时,要尽早停止搜索

这正是剪枝的价值所在。

常见剪枝维度

  1. 数量剪枝
    • 剩余可选元素 < 还需要选的数量 → 直接返回
  2. 数值剪枝(选择元素为正)
    • 当前和已经超过 target
    • 即使选最大值,也无法达到 target
  3. 结构剪枝
    • 不满足合法结构(如右括号多于左括号)

这些剪枝往往都依赖于一个核心性质:

单调性
当前状态不满足条件,向下扩展只会更不满足。


三、典型题目拆解与剪枝思路

77. 组合(Combinations)

目标:从 [1…n] 中选 k 个数

关键剪枝

  • 数量剪枝
    • 剩余数字个数 < 还需要选择的数量 → 直接返回
  • 提前返回
    • 当已选数量 == k,记录答案,不再向下搜索

本质理解

这是一个固定长度的组合问题,搜索树深度是确定的,剪枝点非常清晰。


216. 组合总和 III

目标:从 [1…9] 中选 k 个数,和为 n

关键剪枝

  1. leftTarget < 0
    • 所有数均为正数,后续必然无解
  2. leftTarget > 剩余可选数字的最大可能和
    • 即使全选最大值,也无法凑够
  3. 剩余数字个数 < 还需要选择的数量

关键

  • 同时利用了「一大一小」两种剪枝方向
  • 体现出数值约束与数量约束的对称性

22. 括号生成

目标:生成 n 对合法括号

状态定义

  • left:已使用左括号数量
  • right:已使用右括号数量

剪枝规则

  • left > n → 非法
  • right > left → 非法

本质理解

这道题可以视为一种带结构约束的组合回溯

  • “选” → 加左括号
  • “不选” → 加右括号(但有条件)

本质不是排列,而是合法结构的组合生成


39. 组合总和(可重复选择)

目标:元素可重复选,和为 target

关键剪枝

  1. leftTarget < 0 → 直接返回

  2. leftTarget == 0 → 记录答案并返回

  3. 排序 + 剪枝

    若当前元素 > leftTarget,后续元素更大,可直接 break

可重复选择的表达

  • 使用 dfs(i) 表示当前元素可再次选择
  • 通过起始索引控制是否允许重复

93. 复原 IP 地址

目标:将字符串分割为 4 段合法 IP

强剪枝条件

  1. 字符数量剪枝

    剩余字符数 ∉ [剩余段数, 剩余段数 * 3]

  2. 数值剪枝

    当前段 > 255

  3. 前导零剪枝

    段以 0 开头但长度 > 1

补充思路

  • 由于段数固定为 4
  • 该题也可以用 三重循环 实现
  • 但回溯解法在结构上更统一、可复用性更强

四、方法论总结:如何写好组合型回溯?

推荐解题步骤

  1. 先不剪枝,写出正确解

  2. 明确以下要素:

    状态变量(路径、索引、剩余目标)

    终止条件

  3. 回看搜索树,问自己:

    当前状态已经不可能成功了吗?

  4. 将「不可能成功」的情况提前 return

一个重要认知

剪枝为了利用题目的单调性

  • 当前不满足 → 未来一定不满足
  • 才是可以安全剪枝的前提

五、个人心得体会

  • 组合型回溯并不是新的模型
    它只是对子集型回溯的“条件收敛

  • 实战中:

    先保证正确性、再逐步加剪枝,思路更清晰

  • 多数高质量剪枝,都来源于:

    • 数量边界
    • 数值上下界
    • 结构合法性

当你能一眼看出「当前状态是否还有希望」,回溯题就不再是暴力搜索,而是受控的状态枚举

相关代码

本文涉及的所有代码与笔记,均已同步至我的 GitHub 算法仓库,作为 Java 后端校招过程中的学习记录。