前言
这里记录一下陈菜菜的刷题记录,主要应对25秋招、春招
个人背景
211CS本+CUHK计算机相关硕,一年车企软件开发经验
代码能力:有待提高
常用语言:C++
系列文章目录
第27天 :第七章 回溯算法part01
`
文章目录
一、今日任务
● 理论基础
● 77. 组合
● 216.组合总和III
● 17.电话号码的字母组合
二、详细布置
理论基础
回溯法也可以叫做回溯搜索法,它是一种搜索的方式。
回溯是递归的副产品,只要有递归就会有回溯。
回溯的本质是穷举
,穷举所有可能,然后选出我们想要的答案,如果想让回溯法高效一些,可以加一些剪枝的操作,但也改不了回溯法就是穷举的本质。
回溯法解决的问题
回溯法,一般可以解决如下几种问题:
组合问题:N个数里面按一定规则找出k个数的集合
切割问题:一个字符串按一定规则有几种切割方式
子集问题:一个N个数的集合里有多少符合条件的子集
排列问题:N个数按一定规则全排列,有几种排列方式
棋盘问题:N皇后,解数独等等
回溯法解决的问题都可以抽象为树形结构
,因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度就构成了树的深度
。
递归就要有终止条件,所以必然是一棵高度有限的树(N叉树)
。
回溯三部曲
- 回溯函数模板返回值以及参数
回溯算法中函数返回值一般为void
。
再来看一下参数,因为回溯算法需要的参数可不像二叉树递归的时候那么容易一次性确定下来,所以一般是先写逻辑,然后需要什么参数,就填什么参数。
回溯函数伪代码如下:
void backtracking(参数)
- 回溯函数终止条件
什么时候达到了终止条件,树中就可以看出,一般来说搜到叶子节点了,也就找到了满足条件的一条答案,把这个答案存放起来,并结束本层递归。
所以回溯函数终止条件伪代码如下:
if (终止条件) {
存放结果;
return;
}
- 回溯搜索的遍历过程
回溯函数遍历过程伪代码如下:
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
for循环就是遍历集合区间,可以理解一个节点有多少个孩子,这个for循环就执行多少次
。
backtracking这里自己调用自己,实现递归
。
大家可以从图中看出for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历
,这样就把这棵树全遍历完了,一般来说,搜索叶子节点就是找的其中一个结果了
。
回溯算法模板框架如下:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
77.组合
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
提示:
1 <= n <= 20
1 <= k <= n
样例1:
输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
思路
这题用模板实现。
实战
class Solution {
public:
vector<vector<int>> result;
vector<int> vec;
void backtracking(int n,int k,int begin){
if(vec.size()==k){
result.push_back(vec);
//vec.clear();
return;
}
for(int i=begin;i<=n;i++){
vec.push_back(i);
backtracking(n,k,i+1);
vec.pop_back();
}
}
vector<vector<int>> combine(int n, int k) {
backtracking(n,k,1);
return result;
}
};
踩坑
起始是从1开始,包含n,故递归起始为1.
216.组合总和III
找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
只使用数字1到9
每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
提示:
2 <= k <= 9
1 <= n <= 60
样例1:
输入: k = 3, n = 7
输出: [[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。
思路
这题有了上一题做基础,简单题。
实战
class Solution {
public:
vector<vector<int>> result;
vector<int> vec;
void backtracking(int n,int k,int begin,int sum) {
int i;
//for(i=0;i<k;i++)
// sum+=vec[i];
if (vec.size()==k ) {
if(sum == n)
result.push_back(vec);
return;
}
for (int i=begin;i<=9;i++) {
vec.push_back(i);
sum+=i;
backtracking(n,k,i+1,sum); // 递归
sum-=i;
vec.pop_back();
}
}
vector<vector<int>> combinationSum3(int k, int n) {
backtracking(n,k,1,0);
return result;
}
};
踩坑
sum作为递归的参数比较方便,作为全局变量的话,涉及到修改容易出错。
17.电话号码的字母组合
题目链接:LeetCode17
文章讲解:图文讲解
题目描述
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
提示:
0 <= digits.length <= 4
digits[i] 是范围 [‘2’, ‘9’] 的一个数字
样例1:
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
样例2:
输入:digits = ""
输出:[]
思路
这题也是模板,注意返回值类型。
实战
class Solution {
public:
vector<string> result;
string vec;
string letterMap[10] ={"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
void backtracking(string& digits,int begin) {
if (begin==digits.size()) {
result.push_back(vec);
return;
}
int n=digits[begin]-'0';
string letters=letterMap[n-2];
for (int i=0;i<letters.size();i++) {
vec.push_back(letters[i]);
backtracking(digits,begin+1); // 递归
vec.pop_back();
}
}
vector<string> letterCombinations(string digits) {
if (digits.size() == 0) {
return result;
}
backtracking(digits,0);
//vector<string> res;
return result;
}
};
总结
今天主要学习了回溯的一系列操作,感觉是不熟悉的内容,但是有了模板感觉可以写一写,加油。
加油,坚持打卡的第27天。