leetCode 40.组合总和 II + 回溯算法 + 剪枝 + used数组 + 图解

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

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

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]

(一)解法一:used数组

思路和分析:本题和这道题的 leetCode 39.组合总和 + 回溯算法 + 剪枝 的区别是:

  • 不同点
  1. 本题candidates 中的每个数字在每个组合中只能使用 一次 
  2. 本题数组candidates 的元素是有重复的,而 leetCode 39. 无重复元素的数组candidates
  • 相同点
  1. 解集不能包含重复的组合

总结此题要求:元素在同一个组合内是可以重复的,多少次都可以,但两个组合不能相同。换句话说:集合(数组candidates)有重复元素,但还不能有重复的组合 

举个栗子:candidates = [1, 1, 2], target = 3 (注意前提candidates 已经排序了)

思考(O_O)?为啥used[i-1] == false能表示同一树层candidates[i-1] “使用过” 这种情况呢?

  • 是因为在同一树层used[i-1] == false 能表示当前取的candidates[i]是从candidates[i-1]回溯而来的
  • used[i]==true,表示进入下一层递归,取下一个数,所以在树枝上

 >>问题思考(O_O)?

1).什么是“去重”

  • “去重”:就是使用过的元素不能重复选取

2).何为“树枝去重”“树层去重”(代码随想录Carl老师自创的名词)

可把组合问题抽象为树形结构,used“使用过”)在这个树形结构上是有两个维度的,一个维度表示是同一树枝上使用过,一个维度表示是同一树层上使用过

来看题目要求:“集合(数组candidates重复元素,但还不能有重复的组合 ”。

去重的是同一树层上的“使用过”是不同组合里的元素,而对于同一树枝上的都是一个组合里的元素,不用去重。 

  • 强调注意:在树层去重时,需要对数组排序

>>回溯三部曲:

1).确定回溯函数参数

  • path来收集符合条件的结果
  • result 保存 path,作为结果集
  • startIndex 来控制for循环的起始位置
  • used 是bool型数组,用来记录同一树枝上的元素是否使用过
vector<vector<int>> result;
vector<int>path;
void backtracking(vector<int>& candidates,int sum,int target,vector<bool>&used,int startIndex) 

2).递归的终止条件

  • sum > target 和 sum == targe
if (sum > target) { // 这个条件其实可以省略return;
}
if (sum == target) {result.push_back(path);return;
}================================================================================
可以写成这样:
在递归单层遍历的时候,会有剪枝的操作
for(int i=startIndex;i<candidates.size() && sum + candidates[i] <= target;i++) {...
}

在这篇文章中有提到 for循环剪枝操作sum + candidates[i] <= target为「剪枝操作」。感兴趣的小伙伴们可以看一下:leetCode 39.组合总和 + 回溯算法 + 剪枝icon-default.png?t=N7T8https://blog.csdn.net/weixin_41987016/article/details/134672946?spm=1001.2014.3001.5501

3).单层搜索的逻辑

去重逻辑:if( i>0 && candidates[i] == candidates[i - 1] && used[i - 1] == false),表示前一个树枝,使用了candidates[i - 1],也就是说同一树层使用过candidates[i - 1]。那么此时 for循环 里通过 continue 操作跳过此种情况的递归

for(int i=startIndex;i<candidates.size() && sum + candidates[i] <= target;i++) {if(i>0 && candidates[i]==candidates[i-1] && used[i-1]==false) continue;......
}

C++代码:

class Solution {
public:vector<vector<int>> result;vector<int>path;void backtracking(vector<int>& candidates,int sum,int target,vector<bool>&used,int startIndex) {if(sum == target) {result.push_back(path);return;}for(int i=startIndex;i<candidates.size() && sum + candidates[i] <= target;i++) {/*used[i - 1] == true,说明同一树枝candidates[i - 1]使用过used[i - 1] == false,说明同一树层candidates[i - 1]使用过要对同一树层使用过的元素进行跳过*/if(i>0 && candidates[i]==candidates[i-1] && used[i-1]==false) continue;path.push_back(candidates[i]);sum+=candidates[i];used[i]=true;backtracking(candidates,sum,target,used,i+1);// 和39.组合总和的区别1,这里是i+1,每个数字在每个组合中只能使用一次used[i]=false;sum-=candidates[i];path.pop_back();}}vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {vector<bool> used(candidates.size(), false);// 首先把给candidates排序,让其相同的元素都挨在一起。sort(candidates.begin(),candidates.end());  backtracking(candidates,0,target,used,0);return result;}
};

(二)解法二:不用 used 数组,而用 startIndex 来去重

class Solution {
public:vector<vector<int>> result;vector<int>path;void backtracking(vector<int>& candidates,int sum,int target,int startIndex) {if(sum == target) {result.push_back(path);return;}for(int i=startIndex;i<candidates.size() && sum + candidates[i] <= target;i++) {if(i>startIndex && candidates[i]==candidates[i-1]) continue;path.push_back(candidates[i]);sum+=candidates[i];backtracking(candidates,sum,target,i+1);// 和39.组合总和的区别1,这里是i+1,每个数字在每个组合中只能使用一次sum-=candidates[i];path.pop_back();}}vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {vector<bool> used(candidates.size(), false);// 首先把给candidates排序,让其相同的元素都挨在一起。sort(candidates.begin(),candidates.end());  backtracking(candidates,0,target,0);return result;}
};

参考文章和推荐视频:

代码随想录 (programmercarl.com)icon-default.png?t=N7T8https://www.programmercarl.com/0040.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8CII.html#%E6%80%9D%E8%B7%AF回溯算法中的去重,树层去重树枝去重,你弄清楚了没?| LeetCode:40.组合总和II_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV12V4y1V73A/?p=66&spm_id_from=pageDriver&vd_source=a934d7fc6f47698a29dac90a922ba5a3

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

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

相关文章

AD1668A 双N/P沟道 MOS管 耐压20V 过流2.1A 适用于正反接充电

AD1668A 双N/P沟道 MOS管 耐压20V 过流2.1A 的集成MOS管&#xff0c;封装TSOT23-8封装&#xff0c;体积小&#xff0c;适用于板子较小的板子。相当于2个SI2301、2个SI2302的集成模块。 芯片的内阻 N沟道的基本参数 P沟道的基本参数 这种结构的方式是适用于正反接都能充电的结构…

从0开始学习JavaScript--JavaScript中的解构赋值及使用场景

在现代JavaScript中&#xff0c;解构赋值是一种强大而灵活的语法特性&#xff0c;它允许从数组或对象中提取值并赋给变量。这种语法不仅使代码更简洁&#xff0c;而且提高了可读性。在本篇文章中&#xff0c;将深入探讨JavaScript中解构赋值的基本概念、语法规则以及丰富的使用…

JenKins快速安装与使用

一、JenKins 0.准备&#xff0c;配置好环境 1&#xff09;Git&#xff08;yum安装&#xff09; 2&#xff09;JDK&#xff08;自行下载&#xff09; 3&#xff09;Jenkins&#xff08;自行下载&#xff09; 1.下载安装包 进官网&#xff0c;点Download下方即可下载。要下…

OpenCV项目开发实战--基本图像分割图生成器

欢迎回到我们有关 OpenCV 的系列文章以及我们如何利用其强大的图像预处理功能。在我们之前的文章的基础上,今天我们向您展示如何创建基本的图像分割图生成器。 具体来说,我们的图像掩模应该帮助识别每个像素是否: 背景的一部分(指定值为0)在感兴趣的对象的边缘(指定值 …

相机标定张正友、opencv和halcon对比(1)

本文将从基本标定开始&#xff0c;结合实际工作经验&#xff0c;分析张正友、opencv和halcon三者相机标定的深层原理与不同之处&#xff0c;内容比较多&#xff0c;如果出现错误请指正。 相机光学模型 我们使用的镜头都是由多组镜片组成&#xff0c;它实际上是一种厚透镜模型…

Java—学生信息管理系统(简单、详细)

文章目录 一、主界面展示二、学生类三、系统功能方法3.1 main()方法3.2 添加学生信息3.3 删除学生信息3.4 修改学生信息3.5 查看所有学生信息 四、完整代码4.1 Student .Java4.2 StudentManger.Java 前言&#xff1a;本案例在实现时使用了Java语言中的ArrayList集合来储存数据。…

中国毫米波雷达产业分析3——毫米波雷达市场分析(1~3)

一、总体市场 &#xff08;一&#xff09;总规模 近几年&#xff0c;得益于汽车智能化的高速发展与雷达芯片制作工艺的进步&#xff0c;国内毫米波雷达整体市场增速较快。根据初步测算&#xff0c;2022年中国毫米波雷达市场总规模达到86亿元&#xff0c;实现同比增长24.6%。 图…

【JUC】十六、LockSupport类实现线程等待与唤醒

文章目录 1、LockSupport2、wait和notify存在的问题3、await和signal存在的问题4、park和unpark方法5、LockSupport用法示例6、Permit不会累积7、面试 1、LockSupport 线程等待和唤醒的方式有&#xff1a; 使用Object的wait方法让对象上活动的线程等待&#xff0c;使用notify…

Docker中Alpine容器中配置MariaDB

1.更新镜像源 apk update2.安装 Mysql apk add --no-cache mysql mysql-client # 安装命令也可使用 apk add mariadb mariadb-client&#xff0c;alpine 中 mysql 就是 mariadb3. 安装openrc openrc是Alpine服务控制器&#xff0c;负责Alpine服务启动&#xff0c;添加、删除…

基于ASP.Net的图书管理系统的设计与实现

摘 要 图书馆管理系统是一整套高科技技术与书本管理知识结合的产物。它把传统书籍静态的服务这个缺陷完美化&#xff0c;完成多媒体数据的交互、远程网络连接、检查搜索智能化、多数据库无障碍联系、跨时空信息服务。图书管理系统用计算机程序替代了传统手工记录的工作模式&am…

开源好用EasyImages简单图床源码

开源好用EasyImages简单图床源码分享&#xff0c;虽然它是开源程序&#xff0c;但功能一点也不弱&#xff0c;不仅支持多文件上传、文字/图片水印、支持API和鉴黄、还能自定义代码&#xff0c;最重要的是它不强制使用数据库运行&#xff0c;这就给我们的部署和维护带来极大方便…

深兰科技入选工信部首批“5G+智慧旅游”应用试点项目名

近日&#xff0c;国家文旅部与工信部确定并公布了我国首批《“5G智慧旅游”应用试点项目名单》&#xff0c;深兰科技基于AIGC多模态融合大模型技术开发打造的江汉路“5G智慧旅游”试点项目——武汉市江汉路步行街5G智慧商街创新应用&#xff0c;成功入选该名单。 作为由湖北省文…