回溯法经典练习:组合总和的深度解析与实战

回溯法经典练习:组合总和的深度解析与实战

引言

在算法世界里,回溯法(Backtracking)是解决 组合、排列、子集 等问题的神器。而 “组合总和”(Combination Sum) 问题,更是回溯算法中的经典代表,几乎是算法面试中的常客。

今天,我就带大家从 直觉理解代码实战,深入解析回溯法如何求解 组合总和,并给出代码详解,带你彻底吃透这个问题!


1. 题目解析

🔹 题目描述

给定一个 无重复元素 的正整数数组 candidates,以及一个目标值 target,找到所有可以使数字和为 target唯一组合

要求:

  • 同一个数字可以被无限次使用,但组合的顺序不影响结果(即 {2,2,3}{3,2,2} 视为相同组合)。
  • 结果集不能包含重复的组合。

示例:

输入: candidates = [2,3,6,7], target = 7
输出: [[2,2,3],[7]]
解释: 2+2+3 = 7, 7 = 7,因此答案是 [[2,2,3],[7]]

2. 直觉思考

这个问题的求解方式,可以类比 爬楼梯

  • 每次选择一个数(例如 2 或 3 或 6 或 7)。
  • 判断当前累加和是否等于目标值 target
  • 如果超过 target,则不合法,剪枝回溯。
  • 如果刚好等于 target,就加入结果集。

看到 “所有组合”“无序”“可以重复选择” 这些关键词,回溯法就是最佳选择!


3. 回溯算法核心框架

回溯法的核心思路是 “递归 + 回溯 + 剪枝”,通常分为以下几个步骤:

  1. 终止条件:当当前路径的总和等于 target,将其加入结果集。
  2. 选择操作:从 candidates 里选择一个数,尝试加入当前路径。
  3. 递归调用:累加该数后,继续探索下一个可能的组合。
  4. 回溯撤销选择:探索完该路径后,撤销最后一个选择,回溯到上一步继续尝试其他数。

回溯法的框架:

def backtrack(路径, 选择列表):if 满足结束条件:记录结果returnfor 选择 in 选择列表:做选择backtrack(路径, 选择列表)  # 递归撤销选择  # 回溯

4. 代码实战

🔹 Python 代码

from typing import Listdef combinationSum(candidates: List[int], target: int) -> List[List[int]]:res = []  # 结果集def backtrack(start, path, total):# 递归终止条件:找到一个满足条件的组合if total == target:res.append(path[:])  # 注意要拷贝 pathreturn# 遍历候选数组for i in range(start, len(candidates)):num = candidates[i]# 剪枝:如果 total + num 超过 target,则不再继续if total + num > target:continue# 选择当前数字,递归path.append(num)backtrack(i, path, total + num)  # 这里 `i` 而不是 `i+1`,因为允许重复选择path.pop()  # 回溯,撤销选择# 从索引 0 开始搜索backtrack(0, [], 0)return res

5. 代码详解

🔸 递归逻辑

  • backtrack(start, path, total)
    • start:控制递归深度,避免重复选择前面的数。
    • path:记录当前的组合路径。
    • total:当前路径的累加和。

🔸 关键点

  1. 循环遍历 candidates
    • start 位置开始,避免重复使用已选过的数。
    • 允许重复选择 candidates[i],所以 backtrack(i, ...) 而不是 backtrack(i+1, ...)
  2. 剪枝优化
    • total + num > target 时,提前终止递归,避免不必要的计算。
  3. 回溯撤销选择
    • 递归完一个分支后,撤销 path.append(num) 的选择,继续尝试其他数。

6. 测试代码

candidates = [2, 3, 6, 7]
target = 7
print(combinationSum(candidates, target))

输出结果:

[[2, 2, 3], [7]]

7. 复杂度分析

假设 candidates 长度为 N,目标值 target

  • 最坏情况下,回溯的搜索树是一个 N 叉树,高度为 target/min(candidates)
  • 时间复杂度 近似为 O(2^N)(指数级)。
  • 空间复杂度 近似 O(N)(递归栈空间 + 结果集存储)。

8. 进阶优化

🔹 剪枝优化

在搜索前 先对 candidates 排序,一旦发现 total + num > target,就可以提前停止当前分支,减少不必要的递归:

candidates.sort()  # 先排序,提高剪枝效率

🔹 记忆化搜索

如果 candidates 很多,可以用 字典(哈希表) 存储 target 计算结果,避免重复计算。


9. 结语

回溯法是组合问题的利器,而 “组合总和” 这个问题,是典型的 递归 + 剪枝 题目,掌握它,你就能轻松应对类似的算法题。

🔹 复习总结

  1. 回溯三步走:选择 -> 递归 -> 回溯
  2. 通过 start 参数避免重复i 位置不变以允许重复选取数字。
  3. 剪枝优化:如果 total + num > target,则立即跳过
  4. 理解递归树的结构,有助于更好地 Debug

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/72940.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

传感器研习社:Swift Navigation与意法半导体(STMicroelectronics)合作 共同推出端到端GNSS汽车自动驾驶解决方案

自动驾驶系统单纯依赖感知传感器进行定位在遇到恶劣天气或缺乏车道标线的道路场景时很容易失效。此外,由于激光雷达(LiDAR)、视觉等传感器的成本高昂以及将众多不同组件整合为统一系统的复杂性,都可能增加产品研发成本或延迟产品上…

【人工智能】Ollama 的 API 操作指南:打造个性化大模型服务

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 随着人工智能技术的飞速发展,大型语言模型(LLM)在自然语言处理领域的应用日益广泛。然而,传统的云端模型服务往往面临数据隐私、成本高…

Linux关机重启二三事

、、 1概述 故障是高可用组最常接触的场景,其中包含了进程故障,网络故障、系统故障,硬件故障。掉电、关机和重启作为其中最常见的系统故障,具体的细节还是有些许差异的。本文将从操作系统与主板的行为讲解三者之间的联系与区别。…

算法1--两束求和

题目描述 解题思路 先说一种很容易想到的暴力解法 暴力解法的思路很简单,就是遍历数组,对于每一个元素,都去遍历数组中剩下的元素,判断是否有两个元素的和等于目标值,如果有,就返回这两个元素的下标。 c…

在Fedora-Workstation-Live-x86_64-41-1.4中使用最新版本firefox和腾讯翻译插件让英文网页显示中文翻译

在Fedora-Workstation-Live-x86_64-41-1.4中使用最新版本firefox和腾讯翻译插件让英文网页显示中文翻译 应用——系统工具——终端 suozhangfedora:~$ rpm -aq | grep firefox firefox-131.0.2-1.fc41.x86_64 firefox-langpacks-131.0.2-1.fc41.x86_64 fedora41系统自身安装有f…

android 接入google 登录

在 Android 应用中接入 Google 登录功能,可让用户使用他们的 Google 账号快速登录应用。以下是详细的接入步骤和示例代码: 步骤 1:创建 Google API 项目 访问 Google API 控制台,并使用你的 Google 账号登录。点击 “选择项目”,然后点击 “新建项目”,按照提示填写项目…

Redis缓存与数据库 数据一致性保障

为什么要保证数据一致性 只要使用redis做缓存,就必然存在缓存和DB数据一致性问题。若数据不一致,则业务应用从缓存读取的数据就不是最新数据,可能导致严重错误。比如将商品的库存缓存在Redis,若库存数量不对,则下单时…

19.哈希表的实现

1.哈希的概念 哈希(hash)⼜称散列,是⼀种组织数据的⽅式。从译名来看,有散乱排列的意思。本质就是通过哈希函数把关键字Key跟存储位置建⽴⼀个映射关系,查找时通过这个哈希函数计算出Key存储的位置,进⾏快速查找。 1.2.直接定址法…

IoTDB TTL不生效

问题 时序数据库 IoTDB 1.3.0 版本数据库的 TTL 设置为两天,show databases details 看到设置也是正确的,怎么还是可以查到好几天前的数据?因为有很多不活跃的测点,所以专门设置了两天过期,有什么办法可以自动清理呢&…

【C++基础】Lambda 函数 基础知识讲解学习及难点解析

一、引入 在 C 中,我们通常使用函数来完成特定的功能。但有时候,我们需要在一个函数内部定义一个小型的功能块,这时如果单独写一个函数会显得繁琐。C11 引入了 Lambda 函数,它是一种匿名函数,可以在需要的地方直接定义…

OpenCV 基础模块 Python 版

OpenCV 基础模块权威指南(Python 版) 一、模块全景图 plaintext OpenCV 架构 (v4.x) ├─ 核心层 │ ├─ core:基础数据结构与操作(Mat/Scalar/Point) │ └─ imgproc:图像处理流水线(滤…

iStoreOS软路由对硬盘格式化分区(转化ext4)

一、为什么要格式化分区? 格式化硬盘分区是软路由安装或配置过程中的重要步骤,主要用于清除旧数据、优化文件系统、确保系统稳定性和兼容性。 二、通过iStoreOS硬盘格式化步骤 使用场景:Docker迁移到外置移动硬盘为例,考虑兼容现…

打造用户认证系统,构筑信息安全防线

在当今的数字化时代,信息安全和用户隐私保护变得越来越重要。用户身份认证是确保信息安全的第一道防线。通过验证用户身份,可以防止未经授权的访问和数据泄露。它有助于保护用户的个人信息、账户资金和其他敏感数据。此外,用户身份认证还可以…

北京南文观点:品牌如何抢占AI 认知的 “黄金节点“

在算法主导的信息洪流中,品牌正在经历一场隐蔽的认知权争夺战,当用户向ChatGPT咨询"哪家新能源车企技术最可靠"时,AI调取的知识图谱数据源将直接决定品牌认知排序。南文乐园科技文化(北京)有限公司&#xff…

音视频系列——Websockets接口封装为Http接口

模型服务示例:实时语音转文本服务 本示例展示一个支持双协议(WebSocket流式接口HTTP同步接口)的语音转文本模型服务,并提供将WebSocket接口封装为HTTP接口的代码实现。 一、服务架构设计 #mermaid-svg-nw0dMZ4uKfS4vGZR {font-fa…

Axure项目实战:智慧城市APP(一)(动态面板、拖动效果)

亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! 课程主题:智慧城市APP便民服务平台 主要内容:完整智慧APP原型设计 应用场景:各类政务型、B端APP均可参考 案例展示:&…

MySQL 入门大全:数据类型

🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…

Java 记忆链表,LinkedList 的升级版

文章目录 记忆链表 MemoryLinkedList实战源代码 众所周知,ArrayList 和 LinkedList 是 Java 集合中两个基本的数据结构,对应数据结构理论中的数组和链表。但在这两个数据结构,开发者们通常使用 ArrayList,而不使用 LinkedList。JD…

《白帽子讲 Web 安全》之开发语言安全深度解读

目录 引言 1.PHP 安全 1.1变量覆盖 1.2空字节问题 1.3弱类型 1.4反序列化 1.5安全配置 2Java 安全 2.1Security Manager 2.2反射 2.3反序列化 3Python 安全 3.1反序列化 3.2代码保护 4.JavaScript 安全 4.1第三方 JavaScript 资源 4.2JavaScript 框架 5.Node.…

鸿蒙HarmonyOS NEXT应用崩溃分析及修复

鸿蒙HarmonyOS NEXT应用崩溃分析及修复 如何保证应用的健壮性,其中一个指标就是看崩溃率,如何降低崩溃率,就需要知道存在哪些崩溃,然后对症下药,解决崩溃。那么鸿蒙应用中存在哪些崩溃类型呢?又改如何解决…