算法力扣刷题记录 六十七【40.组合总和II】

news/2024/11/15 6:14:05/

前言

回溯章节第五篇。

记录 六十七【40.组合总和II】。


一、题目阅读

给定一个候选人编号的集合 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]
]

提示:

1 <= candidates.length <= 100
1 <= candidates[i] <= 50
1 <= target <= 30

二、 尝试实现

思路

  1. 理解题目:本题的输入数组candidates中有重复的元素,但是每个元素只能使用一次。求组合,组合不能重复。
  2. (弯路) 如果认为candidates中重复元素不影响直接求结果:先对candidates排序,startindex传给下一层时加1,看起来选了每个元素一次。但是组合出现重复。比如以示例2画图,注意不同颜色的“2”:
    在这里插入图片描述
  3. 所以要搜集每个元素出现的次数,并且把candidates去重得到candidate(没有重复元素)的数组。
  4. 第一步:搜集每个元素出现的次数——用unordered_map;
  5. 第二步:去重得到candidate(没有重复元素)的数组——把unordered_map转换成vector< pair<int ,int>> 类型,pair.first是元素,pair.second是个数。比如示例2这两步之后得到的数组是:
    在这里插入图片描述
  6. 对去重之后的数组candidate求组合。递归函数(回溯操作)开始:
  • 确定递归函数返回值:之前都是用result和temp搜集组合,所以不需要返回值。这里也是。所以void类型;
  • 确定递归函数参数:
    • int startindex:指明下一层从candidate哪个下标开始;
    • int target和int& sum:一个输入target,一个是当前求和sum。
    • result和temp我写到参数里了,可以用全局变量;
    • 操作的对象:去重后的数组candidate。
  • 确定终止条件:当sum == target,搜集结果后return。
  • 确定逻辑:
    • for循环:从startindex开始,到candidate.size()结束。并且sum < target才继续循环。
    • 把元素放入temp,sum做加法,个数减1
    • 当该元素剩余个数 > 0,那么下一层的startindex从i开始;当该元素剩余个数是0,那么下一层的startindex从i+1开始原因:剩余个数大于0,说明该元素相同的值还有能用的,所以下一层从i开始;剩余个数等于0,说明该元素相同的值没有了,所以下一层从i+1开始;
    • 回溯操作:元素弹出temp,sum做减法,个数加1

代码实现

class Solution {
public:unordered_map<int,int> map;void backtracking(int stratindex,int& sum,int target,vector<int>& temp,vector<vector<int>>& result,vector<pair<int,int>>& candidate){//终止条件if(sum == target){result.push_back(temp);return ;}for(int i = stratindex;i < candidate.size() && sum < target;i++){temp.push_back(candidate[i].first);sum += candidate[i].first;candidate[i].second--;if(candidate[i].second > 0){backtracking(i,sum,target,temp,result,candidate);//startindex是i,递到下一层}else{backtracking(i+1,sum,target,temp,result,candidate);}sum -= candidate[i].first;temp.pop_back();candidate[i].second++;}return;}vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {vector<vector<int>> result;vector<int> temp;//中间结果int sum = 0;//统计candidates中每个元素的个数for(int i = 0;i < candidates.size();i++){map[candidates[i]]++;}//去重candidatesvector<pair<int,int>> candidate(map.begin(),map.end());//递归函数backtracking(0,sum,target,temp,result,candidate);return result;}
};

三、参考学习

参考学习链接

学习内容

  1. 第一步:先对candidates排序。将所有相同的值放到一起,方便后面的操作。以下都以示例2——candidates = [2,5,2,1,2]为例:
    在这里插入图片描述
  2. 当前操作对象是排序之后的数组:[1,2,2,2,5]。在选择元素,绘制树形结构时:同一层不能选择相同的值,因为在第一个“2”的分支下,后面所有的元素组合已经搜索;到第二个、第三个……“2”的分支时,后面的元素是重复的。所以第一个“2”分支包含了所有。下一个分支应该从“5”开始。
    在这里插入图片描述
    所以这就是参考所讲的“树层去重”但是“树枝不去重”。
  3. 用used数组表示candidate数组中某个元素有没有被选到temp中,也就是组合中有没有用到它。
  4. 去重逻辑:当该元素和前一个元素相同(同一层),并且前一个元素的used是false(因为i是按顺序增加的,所以前一个是false并且元素值相同说明在第一个“2”就已经搜过了),要continue不处理这个重复分支。
  5. 代码实现
class Solution {
public:void backtracking(int stratindex,int& sum,int target,vector<bool> used,vector<int>& temp,vector<vector<int>>& result,vector<int>& candidate){//终止条件if(sum == target){result.push_back(temp);return ;}for(int i = stratindex;i < candidate.size() && sum+candidate[i] <= target;i++){if(i > stratindex && candidate[i] == candidate[i-1] && used[i] == false){//同一层continue;}temp.push_back(candidate[i]);sum += candidate[i];used[i] = true;//使用过//深入下一层,为了每个元素只使用一次,所以startindex是i+1backtracking(i+1,sum,target,used,temp,result,candidate);sum -= candidate[i];temp.pop_back();used[i] = false;//回归没使用状态}return;}vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {vector<vector<int>> result;vector<int> temp;//中间结果vector<bool> used(candidates.size(),false);//初始化都没有用过int sum = 0;//排序把相同元素放到一起sort(candidates.begin(),candidates.end());//递归函数backtracking(0,sum,target,used,temp,result,candidates);return result;}
};

总结

对==组合问题、39. 组合总和、40.组合总和II(本文)、216.组合总和III==做对比总结。

在这里插入图片描述
(欢迎指正,转载标明出处)


http://www.ppmy.cn/news/1505617.html

相关文章

获取后端返回的文件流 前端进行文件下载

获取后端返回的文件流 前端进行文件下载 1. 在 utils 文件夹下创建 downloadFile.ts import axios from "axios";interface Params {url: string;method: string;data: any; }export const downLoadFileBlob (params: Params) > {return axios({url: params.ur…

Django中事务的基本使用

1. Django事务处理 事务(Transaction): 是一种将多个数据库操作组合成一个单一工作单元的机制. 如果事务中的所有操作都成功完成, 则这些更改将永久保存到数据库中. 如果事务中的某个操作失败, 则整个事务将回滚到事务开始前的状态, 所有的更改都不会被保存到数据库中. 这对于…

【Material-UI】按钮组:按钮变体详解

文章目录 一、按钮变体概述1. 组件介绍2. 基本用法 二、按钮变体详细说明1. 轮廓按钮&#xff08;Outlined&#xff09;2. 文本按钮&#xff08;Text&#xff09;3. 填充按钮&#xff08;Contained&#xff09; 三、按钮变体的实际应用场景1. 界面设计2. 界面一致性3. 视觉层次…

5 倍网络性能提升!DigitalOcean上线全新高级内存优化型和高级存储优化型 Droplet 云主机

支持用户从想法到实现&#xff0c;再到业务不断发展过程中提供持续可靠的支持&#xff0c;这是 DigitalOcean 的核心使命。所以 DigitalOcean 也在不断推出更多专业的解决方案。 DigitalOcean Droplet 是基于虚拟化硬件上运行的虚拟机&#xff08;VM&#xff09;。用户创建的每…

WEB渗透Web突破篇-PHP文件包含下载读取

php任意文件读取/下载 readfile()、file_get_contents()、fopen()等读文件的函数不严谨&#xff0c;读取文件路径可控&#xff0c;输出内容。 下载配置文件 Redis、Weblogic、ftp、mysql、web配置文件、history文件、数据库配置文件 下载log文件 下载web文件 /1.php?f../../e…

环境搭建:如何在 Windows 上安装和配置 Apache Maven 3.9.8

环境搭建&#xff1a;如何在 Windows 上安装和配置 Apache Maven 3.9.8 在 Windows 系统上安装和配置 Apache Maven 是开发 Java 应用程序的关键步骤。本文详细介绍了如何下载合适的 Maven 二进制文件&#xff0c;并正确配置环境变量&#xff0c;使其在命令行中可用。特别适合…

springboot项目不能同时跑junit4和junit5的解决方法

springboot项目的maven test只会跑junit4 RunWith注解的测试类&#xff0c;而不会跑junit5 ExtendWith的测试类 解决方法&#xff1a;pom加上以下plugin&#xff0c;版本号需要3.0.0-M5及以上 <plugin><groupId>org.apache.maven.plugins</groupId><art…