算法练习-组合总和【回溯算法】(思路+流程图+代码)

news2025/1/17 1:12:50

难度参考

        难度:困难

        分类:回溯算法

        难度与分类由我所参与的培训课程提供,但需 要注意的是,难度与分类仅供参考。且所在课程未提供测试平台,故实现代码主要为自行测试的那种,以下内容均为个人笔记,旨在督促自己认真学习。

题目

        给定一个无重复元素的数组candidates和一个目标数target,找出candidates中所有可以使数字和为target的组合,candidates中的数字可以无限制重复被选取。

        说明:

        所有数字(包括target)都是正整数。

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

        示例1:

        输入:candidates =[2,3,6,7],target = 7,

        所求解集为:[[7],[2,2,3]]

        示例2:

        输入:candidates =[2,3,5],target = 8,

        所求解集为:[[2,2,2,2],[2,3,3],[3,5]]

思路

        这个问题是一个典型的组合求和问题,可以通过回溯算法来解决。回溯算法基于试错的思想,它尝试分步的去解决一个问题。在分步解决问题的过程中,当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至是上几步的计算,再通过其他的可能的分步解答再次尝试寻找问题的答案。

        对于这个特定问题,我们可以按照以下步骤来设计我们的算法:

        理解问题

给定一个无重复元素的数组candidates和一个目标数target,找出candidates中所有可以使数字和为target的组合。candidates中的数字可以无限制重复被选取。需要注意的是,解集不能包含重复的组合。

        设计策略

        排序(可选):首先,可以对candidates数组进行排序。这一步是可选的,但有助于优化,因为它可以让我们在某些情况下提前终止搜索。
回溯:使用回溯的方法来逐步构建组合,并且当组合的和等于目标值target时,将其添加到结果集中。
        选择路径:从candidates的开始,逐个尝试数组中的每个数,将其加入到当前组合中,并递归地调用回溯函数,继续尝试下一个数。每次递归调用时,目标值target减去当前选择的数。
撤销选择:如果当前组合的和大于target,或者已经找到一个有效的组合,就回溯,撤销最后的选择,尝试下一个数。
        实现细节

        使用一个辅助函数backtrack来进行回溯搜索。这个函数接受当前的组合combination,当前的开始搜索位置start,和当前的目标值target作为参数。
        当target减到0时,说明找到了一个有效的组合,将其添加到结果集中。
        对于candidates中的每个数,如果它不大于当前的target,就尝试将它加入到当前组合中,并递归地调用backtrack,同时将target减去这个数。每次递归调用后,需要撤销上一步的选择,以便尝试其他的数。
        通过这种方式,我们可以遍历所有可能的组合,找到所有和为target的组合。

示例

        假设我们有一个数组candidates = [2, 3, 6, 7]和一个目标数target = 7,我们需要找到所有组合,使得组合中数字的和为7。

        初始条件

        candidates = [2, 3, 6, 7]
        target = 7
        解决步骤

        开始搜索:

        从数组的第一个元素开始,即数字2。
        尝试数字2:
        将2加入到当前组合中,组合变为[2],target更新为7-2=5。
        再次尝试2,组合变为[2, 2],target更新为5-2=3。
        再次尝试2,组合变为[2, 2, 2],target更新为3-2=1。此时,再加2已经超过目标值,尝试下一个数字。
        尝试数字3:
        从组合[2, 2]开始,尝试加入3,组合变为[2, 2, 3],target更新为3-3=0。
        发现一个有效组合[2, 2, 3],因为它们的和等于原始target7。
        回溯到组合[2],尝试下一个数字。
        尝试数字3:
        从组合[2]开始,尝试加入3,组合变为[2, 3],target更新为5-3=2。
        此时再加3已经超过目标值,尝试下一个数字。
        尝试数字6:
        从组合[2]开始,尝试加入6,但发现2+6已经超过目标值7,因此不再继续尝试。
        尝试数字7:
        从空组合开始,尝试加入7,组合变为[7],target更新为7-7=0。
        发现一个有效组合[7]。
        搜索结束:最终找到的有效组合有[2, 2, 3]和[7]。
        结果

        有效组合1:[2, 2, 3]
        有效组合2:[7]

梳理

        回溯算法的核心在于尝试与撤销的过程,通过这种方式来遍历问题的所有可能解。在上述例子中,我们使用回溯算法来寻找所有和为特定target的组合。这一过程体现在以下几个方面:

        我们从candidates数组的开始尝试每一个数,将其加入到当前的组合中,并更新剩余的target(即target减去当前数的值)。这一步骤相当于在决策树上向下走一步。

        在加入一个数到组合后,我们递归地调用相同的函数来处理更新后的target和当前组合。这一过程相当于在探索决策树的一个分支,直到找到解或者该分支无法继续向下探索(即当前组合的和超过了target)。

        当我们发现当前的组合无法满足条件(和超过target)或者我们已经找到了一个有效的组合,我们需要撤销最后的选择(即从当前组合中移除最后加入的数),然后尝试下一个数。这相当于在决策树上回到上一个节点,并探索另一个分支。

        在上述例子中,当我们尝试组合[2, 2]并继续加入2时,发现和变为6,仍然小于target7。我们再次尝试加入2,此时和变为8,超过了target。于是,我们撤销最后一次加入2的操作,回到和为6的状态(即组合[2, 2]),然后尝试加入3。这个撤销操作就是回溯的体现,它让我们能够回到之前的状态,尝试其他可能的数,直到找到所有满足条件的组合。

        通过这种方式,回溯算法能够系统地探索所有可能的组合,直到找到所有满足条件的解。每当遇到一个死路,算法就会回溯到上一个节点,尝试其他的路径。这种策略确保了算法能够覆盖到决策树的每一个节点,从而找到所有可能的解。

代码

#include <vector> // 包含 vector 头文件
#include <iostream> // 包含输入输出流头文件
#include <algorithm> // 包含算法头文件

using namespace std; // 使用标准命名空间

void backtrack(vector<int>& candidates, int target, vector<vector<int>>& res, vector<int>& combination, int start) {
if (target == 0) { // 如果目标值为0,找到一个有效的组合
res.push_back(combination); // 将组合添加到结果中
return; // 结束此次递归
}

for (int i = start; i < candidates.size() && target >= candidates[i]; ++i) {
    combination.push_back(candidates[i]);  // 选择当前数字
    backtrack(candidates, target - candidates[i], res, combination, i);  // 继续探索
    combination.pop_back();  // 撤销选择
}
}

vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> res; // 存储结果的二维向量
vector<int> combination; // 存储单个组合的一维向量
sort(candidates.begin(), candidates.end()); // 先对候选数字进行排序,有助于提前终止无效的探索
backtrack(candidates, target, res, combination, 0); // 调用回溯函数进行组合求解
return res; // 返回结果
}

int main() {
vector<int> candidates1 = {2, 3, 6, 7}; // 候选数字序列
int target1 = 7; // 目标和
vector<vector<int>> res1 = combinationSum(candidates1, target1); // 调用组合求解函数得到结果
cout << “Example 1:” << endl;
for (const auto& combination : res1) { // 遍历每一个组合
for (int num : combination) { // 遍历组合中的每一个数字
cout << num << " "; // 输出数字
}
cout << endl; // 换行
}

vector<int> candidates2 = {2, 3, 5};  // 候选数字序列
int target2 = 8;  // 目标和
vector<vector<int>> res2 = combinationSum(candidates2, target2);  // 调用组合求解函数得到结果
cout << "Example 2:" << endl;
for (const auto& combination : res2) {  // 遍历每一个组合
    for (int num : combination) {  // 遍历组合中的每一个数字
        cout << num << " ";  // 输出数字
    }
    cout << endl;  // 换行
}

return 0;
}

        时间复杂度:0(2^n),n是数组candidates的长度。

        空间复杂度:o(target)

打卡

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

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

相关文章

相机姿态slovePnP

opencv slovePnP 物体的姿态 估计 物体的姿态&#xff08;位置和方向&#xff09; 通过已知的图像坐标点数组&#xff0c;和对应的世界坐标点数组&#xff0c;相机的内参&#xff0c;畸变参数&#xff0c;求解相机姿态&#xff0c;即旋转向量和平移向量&#xff0c; 例如&…

JSTL标签

JSTL标签 一、什么是JSTL标签&#xff1a; 随着J2EE瘦客户机技术JavaServer Pages(JSP)在过去几年中的流行&#xff0c;开发人员已经创建了许多自定义的JSP标记库。虽然很多标记库是为实现不同目标的而编写的&#xff0c;但它们往往也对迭代、条件及其它通用操作都提供了类似…

解决:“出现问题,Outlook 无法设置你的账户”

原文&#xff1a;https://blog.iyatt.com/?p14213 1 问题描述 Office 专业版 2024 预览版 在 Outlook 输入邮箱后无法进一步配置登录信息&#xff08;腾讯企业邮箱 Exchange 登录&#xff09; 2 解决方法 通过控制面板里的邮箱设置可以正常添加登录&#xff0c;而且能…

链表OJ题第二弹:环形链表和环形链表 II

前言 第一弹的链表题目比较基础&#xff0c;下面两道题目难度升级&#xff0c;可以先自己挑战一下&#xff0c;再来看解析。解析有图示和的文字&#xff0c;有助于你的理解。 1. 环形链表 &#xff08;1&#xff09;题目及示例 给定一个链表的头节点 head &#xff0c;返回…

C语言菜鸟入门·二维数组

C语言菜鸟入门数组简介-CSDN博客 目录 ​编辑 1. 二维数组 1.1 初始化二维数组 1.2 访问二维数组元素 1.3 示例演示 2. 传递数组给函数 2.1 形式参数是一个指针 2.2 形式参数是一个已定义大小的数组 2.3 形式参数是一个未定义大小的数组 2.4 实例演示 1. 二维…

Spring中关于事务的一些方方面面

事务隔离级别&#xff1a; 先了解一些事务隔离级别有哪些&#xff1a; 未提交读(Read Uncommitted)&#xff1a; 允许脏读&#xff0c;也就是可能读取到其他会话中未提交事务修改的数据 提交读(Read Committed)&#xff1a; 只能读取到已经提交的数据。Oracle等多数数据库默…

组态软件在物联网中的应用

随着物联网的快速发展&#xff0c;组态软件在物联网中的应用也越来越广泛。组态软件是一种用于创建和管理物联网系统的可视化工具&#xff0c;它能够将传感器、设备和网络连接起来&#xff0c;实现数据的采集、分析和可视化。本文将探讨组态软件在物联网中的应用&#xff0c;并…

通信入门系列——双边带信号、单边带信号、Hilbert变换

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 本节目录 一、双边带信号 二、单边…

详细分析Pandas中的Series对象(附Demo)

目录 1. 问题所示2. 基本知识3. API Demo4. 示例Demo5. 彩蛋 1. 问题所示 从实战上手基础知识 一开始遇到这个Bug&#xff1a; TypeError: unsupported operand type(s) for -: str and float后面经了解执行减法运算时发生了错误&#xff0c;其中一个操作数是字符串类型&…

文献速递:GAN医学影像合成--用生成对抗网络生成 3D TOF-MRA 体积和分割标签

文献速递&#xff1a;GAN医学影像合成–用生成对抗网络生成 3D TOF-MRA 体积和分割标签 01 文献速递介绍 深度学习算法在自然图像分析中的成功近年来已被应用于医学成像领域。深度学习方法已被用于自动化各种耗时的手动任务&#xff0c;如医学图像的分割和分类&#xff08;G…

React18源码: Fiber树的初次创建过程图文详解

fiber树构造&#xff08;初次创建&#xff09; fiber树构造的2种情况&#xff1a; 1.初次创建 在React应用首次启动时&#xff0c;界面还没有渲染此时并不会进入对比过程&#xff0c;相当于直接构造一棵全新的树 2.对比更新 React应用启动后&#xff0c;界面已经渲染如果再次发…

Java 过滤器深入了解学习

Java 过滤器深入了解学习 生活不能等待别人来安排&#xff0c;要自己去争取和奋斗&#xff1b;而不论其结果是喜是悲&#xff0c;但可以慰藉的是&#xff0c;你总不枉在这世界上活了一场。有了这样的认识&#xff0c;你就会珍重生活&#xff0c;而不会玩世不恭&#xff1b;同时…

【服务发现--service】

1、service的定义 "Service"简写"svc”。Pod不能直接提供给外网访问&#xff0c;而是应该使用service。Service就是把Pod暴露出来提供服务&#xff0c;Service才是真正的“服务”&#xff0c;它的中文名就叫“服务”。可以说Service是一个应用服务的抽象&#…

【软考中级】系统集成项目管理工程师—导学

软考中级——系统集成项目管理工程师 原视频链接&#xff1a;2024年05月【持续更新】最新系统集成项目管理工程师培训课程-网络课程-软考中级培训 考试介绍 基础介绍 软考全称又叫计算机技术与软件专业技师资格水平考试。 软考是国家人力资源和社会保障部、工业和信息化部…

基于SpringBoot实现的医院药品管理系统

一、系统架构 前端&#xff1a;html | layui | js | css 后端&#xff1a;springboot | mybatis-plus 环境&#xff1a;jdk1.6 | mysql | maven 二、代码及数据库 三、功能介绍 01. 登录页 02. 药品库存管理-登记出入口信息 03. 药品库存管理-问题药品信息 …

【机器学习科学库】全md文档笔记:Jupyter Notebook和Matplotlib使用(已分享,附代码)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论人工智能相关知识。主要内容包括&#xff0c;了解机器学习定义以及应用场景&#xff0c;掌握机器学习基础环境的安装和使用&#xff0c;掌握利用常用的科学计算库对数据进行展示、分析&#xff0c;学会使用jupyter note…

字符串(算法竞赛)--Manacher(马拉车)算法

1、B站视频链接&#xff1a;F05 Manacher(马拉车)_哔哩哔哩_bilibili 题目链接&#xff1a;【模板】manacher - 洛谷 ​ #include <bits/stdc.h> using namespace std; const int N3e7; char a[N],s[N]; int d[N];//回文半径函数void get_d(char*s,int n){d[1]1;for(int…

SpringBoot:数据访问-整合 Druid 配置数据源监控

点击查看数据访问demo&#xff1a;LearnSpringBoot06DataJdbc 点击查看更多的SpringBoot教程 简介 Druid Spring Boot Starter 用于帮助你在Spring Boot项目中轻松集成Druid数据库连接池和监控。 一、添加druid-spring-boot-starter依赖 点击查询最新版 <dependency&g…

猜字谜|构建生成式 AI 应用实践(一)

在 2023 亚马逊云科技 re:Invent 之后&#xff0c;细心的开发者们也许已经发现有一个很有趣的动手实验&#xff1a;开发一款可部署的基于大语言模型的字谜游戏&#xff1a; 该款游戏使用了文生图模型为玩家提供一个未知的提示词&#xff0c;玩家需要根据模型生成的图像来猜测该…

【基于Ubuntu20.04的Autoware.universe安装过程】方案二:双系统 | 详细记录 | 全过程图文 by.Akaxi

目录 一、Autoware.universe背景 Part-1&#xff1a;安装双系统教程 二、查看Windows引导方式 三、制作安装盘 四、设置电脑配置 1.关闭bitlocker 2.压缩硬盘分区 3.关闭Secure Boot 4.关闭intel RST 5.BIOS设置U盘引导 五、安装Ubuntu20.04 1.ventoy引导 2.安装配…