代码随想录算法训练营第23天|回溯算法Part02

代码随想录算法训练营第23天|回溯算法Part02

记录题目:

  • 分割回文串
  • 组合总和
  • 组合总和 II

回溯算法是“枚举+剪枝”的经典范式,不同题型的关键在于:是否允许重复使用元素、是否存在重复元素、是否需要固定顺序。

这些因素共同决定了递归的展开方式与剪枝/去重策略,掌握其背后的结构抽象,比记住模板更重要。

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文串。返回 s 所有可能的分割方案。

回溯法的关键在于划分子问题并搜索所有可能解。 本题中,每一次切割构成一层递归,切割点向后推进,直到指针切到字符串末尾,视为一种合法划分。

思路解析

  • 使用回溯法,从左至右尝试所有切割方案;
  • 每次尝试切一段回文串,若合法,则加入路径,继续下一层递归;
  • 切割线位于索引 i,截取子串范围为 [startIndex, i]
  • 注意递归函数需传入 i + 1,避免重复切割相同位置。

给你一个无重复元素的整数数组 candidates 和一个目标整数 target,找出 candidates 中可以使数字和为 target 的所有不同组合,并以列表形式返回。你可以按任意顺序返回这些组合。

candidates 中的同一个数字可以无限制重复选取。如果至少一个数字的被选数量不同,则两种组合视为不同。

实现方式一:通过 startIndex 控制搜索范围

  • 每层递归从当前位置开始向后枚举;

  • 排序的作用:

    • 提前剪枝:若 sum + candidates[i] > target,可以 break 提前终止;

    • 树层去重:相邻相等元素可直接跳过;

  • 本质上是对组合空间进行“逐层展开”,避免重复枚举前面的数。

实现方式二:按值大小剪枝,无需 startIndex

  • 使用路径末尾 path[path.length - 1] 限制当前可选数字不小于上一个, 从而保持非降序组合;

  • 这是一种值域剪枝技巧,可以规避重复组合而不依赖索引;

  • candidates 中数值无重复,故无需考虑值相同的树层去重。

无论采用哪种方式,都必须先对 candidates 进行升序排序,以确保剪枝和去重逻辑有效运行。

类题对比

题目 使用 startIndex 需要排序 说明
77. 组合 否(不强制) 单集合组合,
控制起点防止重复
216. 组合总和 III 否(不强制) 数字范围固定,
使用 startIndex 控制
17. 电话号码的字母组合 否(不需要) 多集合各自独立,
分别递归
39. 组合总和 否(可选) 是(必须排序) 利用数值剪枝,
避免重复组合

给定一个候选人编号的集合 candidates 和一个目标数 target,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

注意:解集不能包含重复的组合。

思路解析

此题与上一题的核心区别在于:

  • 每个元素只能使用一次;
  • candidates 中可能存在重复元素,因此需要进行树层去重

树层去重 vs 树枝去重

解决组合问题的回溯树中,我们常说“同一树层去重,树枝不去重”。这意味着:

  • 树层: 同一层中若某个值已被使用过,则跳过相同值;
  • 树枝: 因为下一层递归来自不同路径,值可重复。

实现关键

  • candidates 排序是去重的前提;

  • 判断去重的条件为: if (i > startIndex && candidates[i] === candidates[i - 1]) continue;

  • 该判断仅作用于同一树层,避免出现重复组合;

  • 注意剪枝条件:若 sum + candidates[i] > target,可立即跳出当前循环。

去重方式:used[]startIndex 的比较

有些题解使用 used[] 配合如下判断: if (i > 0 && candidates[i] === candidates[i - 1] && !used[i - 1]) continue;

这是排列类问题(如 47. 全排列 II )中的典型用法,用于判断“树枝去重”。但在本题(组合问题)中,只要使用 startIndex 控制树层,就足以完成去重,不需要 used[]

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×