- 第 191 篇 -
Date: 2026 - 03- 17| 周二
Author: 郑龙浩(仟墨)
今日算法or技巧:回溯算法
2026-03-17-算法刷题day25
今日算法:回溯算法
本来今天是想把「回溯算法」的所有题都做完的,但是我发现我低估了回溯算法题的难度了
如果纯套模板的可能还能写出来,但凡有变动或者某些特殊条件的,我就会写不出来,很难理解透彻
总体看下来,今天我可以说是什么也没学到:上午满课,然后下午开始搞回溯,真的是把我头整大了,太难理解了
我放弃搞「回溯算法」了,明天开始,做一些「蓝桥杯」真题吧,然后再刷一些基础算法题,不能因为扣这种难以理解的题太长时间,把那些基础的简单题给忘记了,这就得不偿失了,最终再落一个什么也没学到的结果,不值得,还有25天蓝桥杯,希望接下来的25天,我都能把握好每天的学习进度吧。
文章目录
1-力扣17-电话号码的字母组合
【题目】
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例 1:
输入: digits = "23"
输出: ["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:
输入: digits = "2"
输出: ["a","b","c"]
提示:
1 <= digits.length <= 4digits[i]是范围['2', '9']的一个数字。
【思路】
【代码】
cpp
/* 2026-03-17-算法打卡day25
* 1-力扣17-电话号码的字母组合
* Author:郑龙浩
* Date:2026-03-16
* 算法/技巧:回溯算法 & 递归
* 用时:
*/
#include "bits/stdc++.h"
using namespace std;
class Solution {
public:
vector<string> results; // 存储所有字母组合结果的容器
string path; // 当前正在构建的字母组合路径
// 数字到字母的映射表
// 索引0-9对应数字0-9,其中数字2-9对应电话键盘上的字母
string litters[10] = {
"", // 数字0 - 无对应字母
"", // 数字1 - 无对应字母
"abc", // 数字2 - 对应字母abc
"def", // 数字3 - 对应字母def
"ghi", // 数字4 - 对应字母ghi
"jkl", // 数字5 - 对应字母jkl
"mno", // 数字6 - 对应字母mno
"pqrs", // 数字7 - 对应字母pqrs(注意4个字母)
"tuv", // 数字8 - 对应字母tuv
"wxyz" // 数字9 - 对应字母wxyz(注意4个字母)
};
// 主函数:返回所有可能的字母组合
vector<string> letterCombinations(string digits) {
// 处理边界情况:输入为空字符串
if (digits.size() == 0) return results;
// 从第0个数字开始回溯
backtracking(digits, 0);
return results;
}
// 回溯函数
// index: 当前处理到digits中的第几个数字(从0开始)
// 也可以理解为递归树的当前深度
void backtracking(string& digits, int index) {
// 终止条件:已经处理完所有数字
// 当index等于digits长度时,说明已经为每个数字都选了一个字母
if (index == digits.size()) {
results.push_back(path); // 保存当前完整的字母组合
return; // 结束当前递归分支
}
// 获取当前数字对应的字母串
// digits[index]是字符,如'2',减去'0'得到整数2
// 用这个整数作为索引访问litters数组
string s = litters[digits[index] - '0'];
// 遍历当前数字的所有可能字母
for (char ch : s) {
// 做出选择:将当前字母加入路径
path.push_back(ch);
// 递归:处理下一个数字
// index+1表示进入下一层递归,处理digits中的下一个数字
backtracking(digits, index + 1);
// 回溯:撤销选择,回到当前状态
// 为尝试当前数字的下一个字母做准备
path.pop_back();
}
}
};
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
return 0;
}
2-力扣39-组合总和
【题目】
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
示例 1:
输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。
示例 2:
输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:
输入: candidates = [2], target = 1
输出: []
提示:
1 <= candidates.length <= 302 <= candidates[i] <= 40candidates的所有元素 互不相同1 <= target <= 40
【思路】
【代码】
cpp
/* 2026-03-17-算法打卡day25
* 2-力扣39-组合总和
* Author:郑龙浩
* Date:2026-03-16
* 算法/技巧:回溯算法 & 递归
* 用时:17 min
* 说实话做的模模糊糊的,主要是套模板
*/
#include "bits/stdc++.h"
using namespace std;
class Solution {
public:
vector <vector <int>> results;
vector <int> path;
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
backtracking(candidates, target, 0);
return results;
}
int sum (vector <int>& nums) {
int Sum = 0;
for (int it : nums) Sum += it;
return Sum;
}
void backtracking(vector <int>& candidates, int target, int startIndex) {
// 如果sum超过了target继续执行下去,只会无限递归,所以为了避免,直接return
if (sum(path) > target) return; // 刚开始没写这个
if (sum(path) == target) {
results.push_back(path);
return;
}
for (int i = startIndex; i < candidates.size(); i++) {
path.push_back(candidates[i]);
backtracking(candidates, target, i); // 因为可以重复当前数字,所以下一次也从i开始
path.pop_back();
}
return;
}
};
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
return 0;
}
3-力扣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 <= 1001 <= candidates[i] <= 501 <= target <= 30
【思路】
【代码】
cpp
/* 2026-03-17-算法打卡day25
* 3-力扣40-组合总和II
* Author:郑龙浩
* Date:2026-03-16
* 算法/技巧:回溯算法 & 递归
* 用时:1 小时
* 我依然没明白
*
*/
#include "bits/stdc++.h"
using namespace std;
class Solution {
public:
vector <vector <int>> results;
vector <int> path;
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
sort(candidates.begin(), candidates.end()); // 如果要去重,首先要给数组进行排序
vector <bool> used(candidates.size(), false); // 存储遇到过的元素
backtracking(candidates, target, 0, used);
return results;
}
int SUM(vector <int> nums) {
int sum = 0;
for (int it : nums) sum += it;
return sum;
}
void backtracking(vector<int>& candidates, int target, int StartIndex, vector <bool> used) {
int sum = SUM(path);
if (sum > target) return;
if (sum == target) {
results.push_back(path);
return;
}
/* 错误代码:我第一次这么写的
错误原因:没有去重(因为每个数字在每个组合中只能使用一次)
就是使用过的元素,不能在重复选取了
"使用过"有两个维度:1 同一树枝上使用过 2 同一树层上使用过
那么应该去重 同一树枝使用过,还是同一树层使用过?
应该要去重同一树层的"使用过",同一树枝上都是组合中的元素,不需要去重
for (int i = StartIndex; i < candidates.size(); i++) {
path.push_back(candidates[i]);
backtracking(candidates, target, i + 1);
path.pop_back();
}
*/
for (int i = StartIndex; i < candidates.size(); i++) {
// 对于排序后相同的数字,在同一层遍历中,我们只选第一个,跳过后面的相同数字,避免重复组合
if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) continue;
path.push_back(candidates[i]);
used[i] = true;
backtracking(candidates, target, i + 1, used);
used[i] = false; // 回溯
path.pop_back();
}
}
};
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
return 0;
}