代码随想录第二十二天 | Leecode 77. 组合、216. 组合总和 III、17. 电话号码的字母组合
Leecode 77. 组合
题目描述
给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。
你可以按 任何顺序 返回答案。
- 示例 1:
输入:
n = 4
,k = 2
输出:[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
- 示例 2:
输入:
n = 1
,k = 1
输出:[[1]]
解题思路与代码展示
本题要求给出所有满足组合情况,使用穷举法逐个尝试,如果满足则存入结果数组中。但直接写的过程中会遇到两个比较难处理的点:
- 如果每次要求的数的个数固定,比如满足条件的3个数存入数组中,那么就只需使用3层for循环即可,即需要几个数就使用几层for循环。但题目所给的数的个数并不固定,故此时需要使用回溯(递归)的方式来进行处理。
- 此外,还需要确保输出的数组没有重复,即每次搜索过的数不会进行二次搜索。为了避免重复查找,可以设置一个查找的初始值,每次递归的时候传入该值并从这个值开始往后递归搜索。
综合上面思路,可以写出代码如下:
class Solution {
public:
vector<vector<int>> result; // 使用两个全局变量来存储结果值和过程中每一次搜索的结果
vector<int> curVec;
void combineHelper(int n, int k, int startindex){ // 递归函数
if(curVec.size() == k) { // 如果vector当前大小为k,则说明已经符合条件,直接push存放入结果中
result.push_back(curVec);
return;
}
for(int i = startindex; i <= n-(k-curVec.size()) + 1; i++){ // 使用for循环遍历,相当于每次递归调用都有一层for循环;并从startindex开始搜索。其中终止条件中i的上界是剪枝后的结果,排除了一些不可能的情况,可以加速搜索。
curVec.push_back(i); // 将当前数存入当前vector中
combineHelper(n, k, i+1); // 递归调用
curVec.pop_back(); // 回溯退回
}
}
vector<vector<int>> combine(int n, int k) {
combineHelper(n,k,1); // 递归调用辅助函数
return result; // 返回结果
}
};
Leecode 216. 组合总和 III
题目描述
找出所有相加之和为 n
的 k
个数的组合,且满足下列条件:
- 只使用数字
1
到9
- 每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
- 示例 1:
输入:
k = 3
,n = 7
输出:[[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。
- 示例 2:
输入:
k = 3
,n = 9
输出:[[1,2,6], [1,3,5], [2,3,4]]
解释:
1 + 2 + 6 = 9
1 + 3 + 5 = 9
2 + 3 + 4 = 9
没有其他符合的组合了。
- 示例 3:
输入:
k = 4
,n = 1
输出:[]
解释: 不存在有效的组合。
在[1,9]
范围内使用4个不同的数字,我们可以得到的最小和是 1 + 2 + 3 + 4 = 10,因为10 > 1,没有有效的组合。
解题思路与代码展示
本题和上面一题比较类似,使用相似的方法即可,区别在于递归终止条件中要多考虑如果求和值大于目标的情况,此时不满足条件,直接退回即可。
class Solution {
public:
vector<int> curVec; // 使用vector和curSum来记录当前状态,用于判断是否满足条件
int curSum;
vector<vector<int>> result; // result用于存放最终结果
void combHelper(int k, int n, int startNum){
if(curSum == n && curVec.size() == k){ // 如果数求和与数的个数都恰好满足条件,则说明此时已经满足
result.push_back(curVec); // 将满足条件的vector存入结果中
}
else if(curSum > n) return; // 如果求和大于目标值,则一定不满足,此时直接返回即可
for(int i = startNum; i < 10 ; i++){ // 每一位数都从startNum开始递归求解
curSum += i; // 更新当前总和,更新存储的vec向量
curVec.push_back(i);
combHelper(k,n,i+1); // 递归搜索下一个数
curSum -= i; // 回溯,此时要返回上一次的状态,故需要同时恢复curSum和curVec
curVec.pop_back();
}
}
vector<vector<int>> combinationSum3(int k, int n) {
combHelper(k, n, 1);
return result;
}
};
上面代码关键在于递归返回进行回溯时,需要恢复所有递归过程中被修改的变量,在本题中需要同时回复curSum和curVec。
Leecode 17. 电话号码的字母组合
题目描述
给定一个仅包含数字 2-9
的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1
不对应任何字母。
- 示例 1:
输入:
digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
- 示例 2:
输入:
digits = ""
输出:[]
- 示例 3:
输入:
digits = "2"
输出:["a","b","c"]
解题思路与代码展示
本题思路上不算难,在确定了使用回溯的方法后,只需要根据每次数字的不同从相应可能得字母字符中依次选取进行回溯递归即可。但困难的点在于,总共有字母的按键包括2-9总共8个键,如果每一个键用一个if,同时内部使用一个for循环进行回溯,会导致写很多没必要的重复代码。为了避免这种情况需要使用一些方法来进行处理。我首先想到的第一种方法是合并前边2-6个数字,因为前5个数每个数都刚好对应3个字母,因此可以使用乘除法和取余运算来进行计算当前字母对应哪些字母。但需要注意的是,这个计算过程中需要非常小心int类型与char类型之间的转换,1的ASCII码并不等于1,中间需要加减一个'0'
才能进行转换。
使用这种方法可以写出代码如下:
class Solution {
public:
vector<string> result; // 同样是使用全局变量来存储结果和当前状态的字符串
string curStr;
void combinationHelper(string digits, int curIndex){
if(curIndex == digits.size()) { // 如果所有数字已经遍历结束,则将当前字符串存入结果中,并返回
result.push_back(curStr);
return;
}
if(digits[curIndex] < '7'){ // 对于前2-6的数,每个数都对应三个字母,故可以放在一起处理
for(int i = 0; i < 3; i++){ // for循环遍历每个数对应的三个字母
curStr += ('a' + (digits[curIndex]-'0'-2)*3 ) + i%3; // 存入一个字母,此时要注意字符与int之间转换需要减去一个值
combinationHelper(digits, curIndex+1); // 递归调用存入下一个数字对应的字母
curStr.pop_back(); // 回溯
}
}
else if(digits[curIndex] == '7' ){ // 处理完前2-6之后,采用同样的方式处理7,8,9,区别在于7和9对应4个字母,其余都一样
for(int i = 0 ; i < 4; i++){
curStr += 'p' + i;
combinationHelper(digits, curIndex+1);
curStr.pop_back();
}
}
else if(digits[curIndex] == '8'){
for(int i = 0; i < 3; i++){
curStr += 't' + i;
combinationHelper(digits, curIndex+1);
curStr.pop_back();
}
}
else if(digits[curIndex] == '9'){
for(int i = 0 ; i < 4; i++){
curStr += 'w' + i;
combinationHelper(digits, curIndex+1);
curStr.pop_back();
}
}
}
vector<string> letterCombinations(string digits) {
if(!digits.size()) return result; // 如果输入的字符串为空,则直接返回
combinationHelper(digits, 0); // 使用回溯递归调用,将所有可能结果都存入result
return result; // 返回result结果
}
};
可以看到,上面代码虽然确实在合并了一些情况的同时也能够处理本题中的所有情况,但是对于数字取到7-9时,还是有大量重复代码。为了简化这部分内容,考虑使用一个string类型的数组来存放每个数字对应的字母。即:
string numLetterMap[10]{
"",
"",
"abc",
"def",
"ghi",
"jkl",
"mno",
"pqrs",
"tuv",
"wxyz"
};
这样每次使用将按键数字转换为字母字符的时候,直接去数组中查找即可,这样我们可以修改得到下面的代码:
class Solution {
public:
string numLetterMap[10]{ // 存放每个按键对应的字母字符
"",
"",
"abc",
"def",
"ghi",
"jkl",
"mno",
"pqrs",
"tuv",
"wxyz"
};
void combHelper(vector<string>& result, string digits, string curStr){
if(curStr.size() == digits.size()){ // 如果已经长度相等,说明已经完成,此时需要存入结果中
result.push_back(curStr);
return;
}
int num = digits[curStr.size()] - '0'; // 首先将char类型的数字转换为int类型
for(int i = 0; i < numLetterMap[num].size(); i++){ // for循环下遍历所有取值,注意此时循环次数取决于字符串数组中对应位置的长度
curStr += numLetterMap[num][i]; // 当前字符串上加一个字母
combHelper(result, digits, curStr); // 递归
curStr.pop_back(); // 回溯
}
}
vector<string> letterCombinations(string digits) {
vector<string> result;
if(!digits.size()) return result;
string curStr = "";
combHelper(result, digits ,curStr);
return result;
}
};
通过上面代码可以学到,之后再遇到讨论情况比较多的时候,为了避免使用多个if或是switch等造成讨论情况过多,可以考虑使用一些数据类型来存储一部分信息,从而简便讨论。
今日总结
今天学习到了回溯算法,了解到回溯和递归的密不可分。回溯过程中会有一个或多个变量用于存放“当前状态”,每一次使当前状态变为下一状态之后调用递归函数进行处理,递归结束之后还需要将当前进行恢复。同时回溯可以考虑使用一个全局变量来存放结果和当前状态,或者是用引用传递的方式来确保可以进行修改。
此外,当遇到需要讨论的情况比较多的时候,考虑能否使用一个数组来存储不同情况下的一些取值,从而减少if讨论分支,减少代码量。
力扣当前刷到82题了,继续坚持
代码随想录第二十二天 | Leecode 77. 组合、216. 组合总和 III、17. 电话号码的字母组合的更多相关文章
- LeetCode ● 216.组合总和III ● 17.电话号码的字母组合
LeetCode 216.组合总和III 分析1.0 回溯问题 组合总和sum == n 时以及path中元素个数 == k 时,res.add(new path) 返回后递归删除掉当前值 class ...
- 代码随想录第二天| 977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II
2022/09/22 第二天 第一题 这题我就直接平方后排序了,很无脑但很快乐啊(官方题解是双指针 第二题 滑动窗口的问题,本来我也是直接暴力求解发现在leetCode上超时,看了官方题解,也是第一次 ...
- javaSE第二十二天
第二十二天 312 1:登录注册IO版本案例(掌握) 312 2:数据操作流(操作基本类型数据的流)(理解) 313 (1)定义: 313 (2)流对象名称 313 (3 ...
- 《代码大全(第二版)》【PDF】下载
<代码大全(第二版)>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382264 内容简介 <代码大全(第2版)>是著 ...
- 孤荷凌寒自学python第二十二天python类的继承
孤荷凌寒自学python第二十二天python类的继承 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) python中定义的类可以继承自其它类,所谓继承的概念,我的理解 是,就是一个类B继承自 ...
- [LeetCode] 216. Combination Sum III 组合之和 III
Find all possible combinations of k numbers that add up to a number n, given that only numbers from ...
- 代码随想录第十三天 | 150. 逆波兰表达式求值、239. 滑动窗口最大值、347.前 K 个高频元素
第一题150. 逆波兰表达式求值 根据 逆波兰表示法,求表达式的值. 有效的算符包括 +.-.*./ .每个运算对象可以是整数,也可以是另一个逆波兰表达式. 注意 两个整数之间的除法只保留整数部分. ...
- 代码随想录第八天 |344.反转字符串 、541. 反转字符串II、剑指Offer 05.替换空格 、151.翻转字符串里的单词 、剑指Offer58-II.左旋转字符串
第一题344.反转字符串 编写一个函数,其作用是将输入的字符串反转过来.输入字符串以字符数组 s 的形式给出. 不要给另外的数组分配额外的空间,你必须原地修改输入数组.使用 O(1) 的额外空间解决这 ...
- 【LeetCode动态规划#05】背包问题的理论分析(基于代码随想录的个人理解,多图)
背包问题 问题描述 背包问题是一系列问题的统称,具体包括:01背包.完全背包.多重背包.分组背包等(仅需掌握前两种,后面的为竞赛级题目) 下面来研究01背包 实际上即使是最经典的01背包,也不会直接出 ...
- ruby代码重构第二课
(文章都是从我的个人主页上粘贴过来的, 大家也可以访问我的主页 www.iwangzheng.com) 在第一课里提取出了相通的代码,第二课里就把常量提取出来吧 一般把常量的定义写的对应的app/mo ...
随机推荐
- Luogu P1777 帮助 题解 [ 紫 ] [ 线性 dp ] [ 状压 dp ]
帮助:大毒瘤!!!调了我2h,拍了我2h,最后没调出来,重写才AC.wdnmd. 思路 这题主要是线性 dp ,而状压 dp 只是最后在统计答案时的一个辅助. 首先定义 \(dp[i][j][k]\) ...
- 开源EFCore 对比实体与实际数据库结构的工具-GZY.EFCoreCompare
前言 GZY.EFCoreCompare 是一个用于 对比数据库结构和 EF Core 代码中的 DbContext 的库. 它基于 EF Core 的 Scaffolding 机制,生成 Datab ...
- 使用mongodb、Kafka保存mqtt消息
一.引言 随着物联网技术的迅猛发展,大量的设备和传感器产生了海量的数据.本文利用了 MQTT.Kafka 和 MongoDB 各自的优点,满足实时数据处理和大规模数据存储的需求. 如图: 二.总结 优 ...
- 5090D-deepseek-Anythingllm-Ollama运行测试
ollama ollama配置环境变量 ollama地址与镜像 C:\Users\DK>curl http://10.208.10.240:11434 Ollama is running C:\ ...
- 浅谈李飞飞巴黎演讲:如果 AI 资源被少数公司垄断,整个生态系统都会完蛋
在巴黎人工智能峰会开幕式上,斯坦福大学教授.人工智能专家李飞飞发表了主题演讲,揭示了人工智能如何从"观察者"转变为重塑世界的"行动者".她在致辞中,分析了&qu ...
- Typecho如何添加微博表情包
自从添加了蛆音娘表情包就想着去爬点其他地方的表情包- 使用教程跟蛆音娘一样 :点我查看 #表情包代码: "微博":{ "type": "usr&quo ...
- [Windows] 联发科秒开bl一键版(mtk)
声明 不是所有的联发科都可 天机 8000 8100 9000等不行 已知 天机820 天机1000 mtk G90t 天机800 可以 其余自己测试 除了新款均可 第一步 下载软件 (是个压缩包需要 ...
- FolderMove:盘符文件/软件迁移工具,快速给C盘瘦身
前言 很多朋友安装软件的时候总会直接点击下一步,每次都把软件安装到了C盘.时间长了以后系统C盘就会爆满,只能重做系统处理,有了这个软件就可以随时把C盘文件转移到其他分区 介绍 这款是国外软件,界面介绍 ...
- Docker - 部署禅道
原文链接:https://mp.weixin.qq.com/s/8L0Rv6Wc0lFsQU6Lw0QloQ 简单的看了一下原文,他使用的是Ubuntu的操作系统,第一步的Docker安装相关命令 ...
- ETL工程师
Python Flume DataX HDFS 数仓建模分层:ODS.DIM.DWD.DWS.APS Kettle.Informatica SQL(Oracle.MySQL)