当前位置: 首页 > news >正文

python 组合求和 (回溯-中等)含源码(十七)

问题说明(含示例)

问题描述:给定一个无重复元素的整数数组 candidates 和一个目标整数 target,找出 candidates 中可以使数字和为 target 的所有不同组合。要求同一数字可无限制重复选取,且两种组合若至少一个数字的选取数量不同,则视为不同组合;最终结果以列表形式返回,顺序可任意,且保证组合数少于 150 个。

示例

输入输出解释
candidates = [2,3,6,7], target = 7[[2,2,3],[7]]2 可重复选取,2+2+3=7;7 直接选取,7=7,仅这两种组合满足条件
candidates = [2,3,5], target = 8[[2,2,2,2],[2,3,3],[3,5]]2 重复选 4 次(2×4=8)、2 选 1 次 + 3 选 2 次(2+3×2=8)、3 选 1 次 + 5 选 1 次(3+5=8),共三种组合
candidates = [2], target = 1[]2 > 1,无法通过任何选取得到和为 1 的组合,返回空列表

解题关键

核心思路是利用回溯法,通过 “选择 - 递归 - 撤销” 的逻辑探索所有可能组合,同时通过 “起始索引控制” 避免顺序重复(如 [2,3] 和 [3,2]),通过 “剪枝” 减少无效计算,具体步骤如下:

  1. 特殊情况处理

    • 若 candidates 为空或 target <= 0,直接返回空列表(无有效组合);
    • 过滤 candidates 中大于 target 的数字(选取后必然超目标,无需进入递归,提前剪枝)。
  2. 初始化变量

    • result:用列表存储所有符合条件的组合(最终返回结果);
    • 回溯函数参数:
      • current_sum:当前组合的数字和(初始为 0);
      • current_list:当前正在构建的组合(初始为空列表);
      • start:当前选取数字的起始索引(初始为 0,用于控制 “不回头选前面的数字”,避免重复组合)。
  3. 回溯核心步骤(递归实现):

    • 终止条件 1:若 current_sum == target,将 current_list 的副本加入 result(避免引用修改),直接返回;
    • 终止条件 2:若 current_sum > target,直接返回(剪枝,无需继续累加);
    • 遍历选择:从 start 开始遍历过滤后的 candidates(避免回头选,防止重复);
      • 选择:将当前数字 value 加入 current_list,并累加 current_sum
      • 递归:调用回溯函数,传入更新后的 current_sumcurrent_list,且 start 设为当前索引 i(允许重复选当前数字)
      • 回溯:撤销选择,即 current_sum 减去 valuecurrent_list 移除最后一个元素(恢复状态,尝试下一个数字)。
  4. 启动与返回:调用回溯函数(初始参数 current_sum=0, current_list=[], start=0),最终返回 result

对应代码

from typing import Listclass Solution:def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:# 特殊情况处理:空数组或目标无效,直接返回空列表if not candidates or target <= 0:return []# 提前剪枝:过滤大于target的数字(修正:曾未过滤,增加无效计算)valid_candidates = [num for num in candidates if num <= target]result = []  # 存储所有有效组合def backtrack(current_sum: int, current_list: List[int], start: int) -> None:# 终止条件1:和等于目标,保存组合副本(修正:曾漏写copy()或误写copy)if current_sum == target:result.append(current_list.copy())  # ✔ 正确:调用copy()保存副本# ✘ 曾错误1:result.append(current_list) → 直接加引用,结果被污染# ✘ 曾错误2:result.append(current_list.copy) → 加方法对象,类型错误return# 终止条件2:和超过目标,剪枝(避免无效计算)if current_sum > target:return# 从start开始遍历,避免回头选(修正:曾从0开始,或递归传错参数)for i in range(start, len(valid_candidates)):value = valid_candidates[i]# 1. 选择:累加和 + 加入组合current_sum += valuecurrent_list.append(value)# 2. 递归:传i而非start,允许重复选当前数字,且不回头(修正:曾传start)backtrack(current_sum, current_list, i)  # ✔ 正确:传i# ✘ 曾错误:backtrack(current_sum, current_list, start) → 传start导致重复# 3. 回溯:撤销选择,恢复状态current_sum -= valuecurrent_list.pop()# 初始调用:和0、空组合、起始索引0backtrack(0, [], 0)return result

对应的基础知识

实现组合总和算法需掌握以下核心基础概念,是理解代码逻辑的前提:

1. 回溯法的 “三步框架”

回溯法是探索 “多路径选择” 问题的通用模板,核心是 “尝试 - 回退 - 再尝试”,对应代码中的三步:

  • 选择:将当前数字加入组合(current_list.append(value)),更新当前和(current_sum += value);
  • 递归:基于当前选择,探索后续可能的组合(backtrack(...));
  • 回溯:撤销当前选择(current_list.pop()current_sum -= value),回到上一步尝试其他数字。这一框架适用于排列、组合、子集等 “穷举类” 问题。

2. 列表的 “引用特性” 与 copy() 的必要性

Python 中列表是可变对象(引用类型),若直接将 current_list 加入 result,后续对 current_list 的 append/pop 会同步修改 result 中的元素(例如 current_list 后续添加数字,已存入的组合也会跟着变化)。通过 current_list.copy() 创建列表副本,副本的修改不会影响原列表,确保 result 中存储的是固定、完整的组合。

3. “起始索引 start” 的核心作用

start 的唯一目的是避免顺序重复的组合(如 [2,3] 和 [3,2]):

  • 遍历从 start 开始,意味着 “仅能选择当前数字及之后的数字”,无法回头选前面的数字;
  • 例:选了 3(索引 1)后,后续只能选 3 及之后的数字(如 5),不会再选 2(索引 0),从而杜绝 [3,2] 这类与 [2,3] 重复的组合。

4. 剪枝的基本思想

“剪枝” 是减少无效计算的优化手段,代码中两处体现:

  • 提前过滤valid_candidates 排除大于 target 的数字(如 target=7 时排除 8),避免这些数字进入递归;
  • 终止剪枝current_sum > target 时直接返回(如 current_sum=8target=7 时,无需继续加数字),减少不必要的递归调用。

对应的进阶知识

组合总和问题的背后,涉及算法复杂度、回溯优化与问题本质的深层理解:

1. 时间与空间复杂度分析

  • 时间复杂度O(n^(k))n 为 valid_candidates 长度,k 为组合的最大长度,k = target / min(valid_candidates))组合的最大长度由 “最小数字” 决定(例如 target=7、最小数字 2,最大组合长度为 4,即 2×4=8>7);每个步骤有 n 种选择(但因 start 控制,实际分支数递减),总时间与递归树的节点数成正比,即 O(n^k)

  • 空间复杂度O(k)k 为组合的最大长度)

    • 递归调用栈:深度等于组合的最大长度 k(构建最长组合需 k 层递归);
    • 当前组合列表:current_list 的最大长度为 k;注意:存储结果的 result 列表属于 “输出必要空间”,不计入算法额外空间。

2. “重复选数字” 与 “避免重复组合” 的平衡

  • 允许重复选数字:通过递归时传递 start=i 实现 —— 选了当前数字 value(索引 i)后,下次仍能从 i 开始选,即重复选 value
  • 避免重复组合通过 start 控制遍历范围实现 —— 不能选 i 之前的数字,避免因顺序不同导致的重复(如 [2,3] 和 [3,2])。这一设计精准平衡了 “重复选” 和 “去重” 的需求,无需额外的 “已选标记数组”(如 used),简化代码且提升效率。

3. 回溯法与迭代法的对比

除回溯法外,组合总和也可通过 “迭代法” 实现(用队列 / 栈存储每个组合的状态),但两者各有优劣:

对比维度回溯法迭代法
代码可读性高(直观体现 “选择 - 回溯” 逻辑)低(需手动维护队列中的状态,如当前和、组合、起始索引)
空间效率高(仅用递归栈和当前组合列表)低(队列需存储所有中间状态,空间占用与中间组合数成正比)
适用场景优先用于 “需要回溯探索” 的问题(如组合、子集)适用于递归深度过大(可能栈溢出)的场景
实际解题中,回溯法因代码简洁、逻辑清晰,是组合总和的首选方案。

4. 边界情况的完整性处理

代码通过两处边界判断确保逻辑完整:

  • 空输入 / 无效目标if not candidates or target <= 0 直接返回空列表,避免无效递归;
  • 超目标剪枝if current_sum > target 直接返回,避免继续累加数字(如 current_sum=7 时,再加任何正数都会超目标)。边界处理是算法 “正确性” 的关键,可避免漏解或错解(如空输入返回空列表,而非报错)。

编程思维与启示

组合总和的解题过程,体现了多种通用编程思维,可迁移到 “组合总和 Ⅱ(含重复元素)”“目标和” 等类似问题:

1. “问题拆解” 的简化思路

将 “找和为 target 的组合” 这一复杂问题,拆解为 “选当前数字 / 不选当前数字” 的子问题:

  • 选当前数字:加入组合,累加和,递归探索后续数字;
  • 不选当前数字:回溯后跳过当前数字,遍历下一个数字;通过子问题的逐一解决,降低整体问题的复杂度,符合 “分而治之” 的编程思想。

2. “效率优先” 的剪枝意识

剪枝是提升回溯算法效率的核心手段,代码中 “提前过滤大于 target 的数字” 和 “超目标时终止”,本质是 “主动排除无效路径”:

  • 提前过滤:从源头上减少递归的输入规模(如 candidates 有 100 个数字,过滤后可能只剩 10 个);
  • 终止剪枝:在递归中途终止无效路径(如 current_sum=8target=7 时,无需继续递归);这种 “能省则省” 的剪枝意识,在处理大规模输入时尤为重要。

3. “状态控制” 的精准性

回溯法的核心是 “状态的维护与恢复”:

  • 维护状态:current_sum 和 current_list 记录当前组合的核心信息;
  • 恢复状态:current_sum -= value 和 current_list.pop() 撤销选择,确保回溯后状态与之前一致;若状态恢复不完整(如漏写 current_sum -= value),会导致后续计算的和错误,直接引发结果偏差。

4. “去重逻辑” 的灵活设计

组合问题的 “去重” 无需依赖额外数据结构(如集合),可通过 “索引控制” 实现:

  • 与 “全排列” 的区别:全排列需用 remaining 或 used 确保每个数字只用一次,组合问题需用 start 确保不回头选;
  • 优势:相比 “用集合去重”(需生成所有组合后再去重),“索引控制” 在递归过程中直接避免重复,时间效率更高;这一设计体现了 “根据问题特性选择最优去重方式” 的思维,而非依赖通用工具。
http://www.dtcms.com/a/493082.html

相关文章:

  • 注册域名哪个网站好京东的网站建设
  • 怎么用织梦模板做网站短视频动画制作
  • 重庆网站域名备案地址东营区住建行业信用平台
  • 2013电子商务网站建设考试试卷网站三d图怎么做
  • 网站建设平台计划书开发网站网络公司怎么样
  • 山西智能网站建设制作免费建设小学校网站
  • 【ROS2】创建自定义接口
  • php网站接口开发上海十大网站建
  • 个人网站开论坛本地创建wordpress
  • 深圳网站优化课程哪里学域名申请成功后怎么做网站
  • 临安营销型网站建设罗湖建设网站
  • 移动端的网站怎么做微信开放平台官方网站
  • 宁波网络公司网站建设项目o2o网站开发框架
  • 物流网站建设规划总结网站建设预付流程
  • 济南学习网站制作杭州家装口碑比较好的公司
  • 建设部网站查询通报网站制作时间代码
  • 怎样做网站建设公司网站制作找哪家
  • 建设网站需要分析什么织梦怎么在本地编辑多个网站
  • 杭州做网站找力果机械设计平台
  • 中山网站建设网站河北网站备案系统
  • 企业网站建设的方案书宁波网站优化公司
  • 制作俄语网站mip 网站
  • 网站中文域名到期个人注册的网站可以做公司宣传用吗
  • 沪浙网站网络营销策划方案ppt
  • 江苏做网站公司wordpress即时
  • 如何找做网站的公司wordpress爱找主题
  • 网站个人主页怎么做建站快车打电话
  • 网站按钮设计成什么颜色原因网站可以做弹窗广告么
  • No商业网站建设软件汇
  • 做poster网站上海最新新闻事件今天国内