既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
有些事情本来很遥远,你争取,它就会离你越来越近
概念 + 名词解释
递归
是指在一个函数的定义中调用自身的过程,有些复杂问题可以划分为多个相同子问题的话,就可以大概率可以使用递归来解决。经典算法如暴搜
、回溯
、深度优先遍历(dfs)
、FloodFile算法
、记忆化搜索
等本质上都是使用递归来解决,换句话来说,这些本质上来说都是一种问题。
如何理解递归
初学者可以在做递归简单问题的时候,画递归展开图来帮助理解递归,如斐波那契数列问题 f(n) = f(n - 1) + f(n - 2),要求出f(4),就得先递归去求解 f(3)和 f(2);
当学习了数据结构之后呢,可以做一些二叉树相关
的 OJ 题目去理解递归,但二叉树刚开始做其实都是非常迷的,但慢慢理解后,好像确实就只是这么个事哈哈。
当做了一些简单递归题目和二叉树后,就可以尝试从宏观上理解递归了,可以将递归问题分为三步:
- 不细想递归展开图,越复杂的问题越不能画递归展开图,递归展开图只适用于在简单题中理解递归
- 把递归的函数想象成一个
黑盒
,你让这个黑盒去完成一个任务,并信任这个黑盒一定能完成。只需要设计好这个黑盒即可! - 将复杂问题的主逻辑写出来,只在乎递归的结果,并将这个问题解决
那么问题就变为了,如何设计递归函数,也就是如何设计一个这样的黑盒?
首先,找到题目中的多个相同子问题——递归函数头的设计
只关心其中一个子问题是如何解决的——递归函数体的设计
根据经验或题目要求再确定一下递归的出口即可
单看比较抽象,使用一个例子来解释一下上面的步骤:
面试题——汉诺塔
汉诺塔问题,大家都听过吧,将多个圆盘从一个柱A 借助 柱B 移动到 柱C,前提是不小的盘子必须放在大的盘子的上面,问一共有多少种解法?
当然汉诺塔问题是有公式解决的的,但这没有任何意义,我们的目的是从宏观上理解递归。
当有n个圆盘的时候,我们可以先将(n-1)个圆盘移动到 塔2 上面,然后再将塔1的最后一个大圆盘移至 塔3,再用同样的方法将塔2上的(n-1)个元素借助塔1移至塔3,要将这 n - 1 个移动,就要先移动 n - 2,如此往复…
找到了重复子问题:要移动 n 个,就要先解决 n - 1 个怎么搞,要解决 n - 1 个,就要先解决 n - 2 个怎么搞…,并且函数头需要将这几个柱子全部传过去
那么其中一个子问题需要怎么解决呢?
假设有 3个柱子ABC吗,n 个盘子,那么需要先将 n - 1 个盘子借助 C 先移动到 B,再将地下那个最大的移动到 C ,然后A 空了,再借助 A 将 B 上的 n - 1 个盘子移动到 C上,就完成了任务。说明在 n 减到 1 之前,都需要重复上面的步骤,先将 n -1 个盘子借助一个柱子移动到另一个上,然后将第 n 个移动。那么函数体的设计:
那么,在 n > 1 的时候就要一直递归,直到 n = 1 时才进行第一次返回,所以递归出口就是 n == 1,递归返回前,需要将盘子从 A 移动到 C,然后返回。
汉诺塔力扣代码:
class Solution {
public:
void \_haota(vector<int>& A, vector<int>& B, vector<int>& C, int n)
{
if(n == 1)
{
C.push\_back(A.back());
A.pop\_back();
return;
}
\_haota(A, C, B, n - 1);
C.push\_back(A.back());
A.pop\_back();
\_haota(B, A, C, n - 1);
}
void hanota(vector<int>& A, vector<int>& B, vector<int>& C) {
int n = A.size(); // 需要移动的盘子数量
\_haota(A, B, C, n);
}
};
其实我也不知道自己到底说没说明白…
dfs 回溯类问题
步骤:
先使用决策树画出所有情况,如对 [1, 2, 3] 进行全排列
使用 全局变量path 来记录某条路径上的结果,在决策树画到底部的时候,判断 path 中的结果是否符合结果,然后需要“向上返回”,并且顺便“恢复现场”,如上图中,path 中在底部变成了 123,恢复现场后就成了 12;如果没有可以继续走的路,就继续“向上返回”,再恢复现场,然后判断是否有其他路径可以走,遍历完决策树中所有情况后,就暴搜出了所有情况。
剪枝
即剪去不合理的分支,当全排列的时候,某个数字被列举了之后,就不能再使用了,因此要剪去这个分支!一般我们可以使用 bool 类型的check[] 数组来实现剪枝。
全排列力扣代码
class Solution {
public:
vector<vector<int>> ret;
vector<int> path;
bool check[7] = { false };
vector<vector<int>> permute(vector<int>& nums) {
dfs(nums);
return ret;
}
void dfs(vector<int>& nums)
{
if(path.size() == nums.size())
{
ret.push\_back(path); // 将合理路径放入最终结果
return;
}
for(int i = 0; i < nums.size(); i++)
{
if(!check[i])
{
check[i] = true;
path.push\_back(nums[i]);
dfs(nums);
check[i] = false; // 恢复现场
path.pop\_back(); // 恢复现场
}
}
}
};
蓝桥杯——飞机降落
这道题的数据要求不算高,我们就可以考虑将所有情况都枚举一遍,也就是暴搜!
#include<bits/stdc++.h>
using namespace std;
// 飞机降落
struct plane
{
int a; // 到达时间
int b; // 可以盘旋的时间
int c; // 降落所需的时间
};
int T; // 组数
int N; // 每组的飞机数
plane vp[12];
bool check[12];
bool dfs(int num, int last)//num 为第几个, last 为上一架的降落时间
{
if(num == N)
{
return true;// 能走到这一步,说明符合题意且结束了
}
for(int i = 0; i < N; i++)
{
if(!check[i] && vp[i].a + vp[i].b >= last)// 说明可以降落
{
check[i] = true;
if(dfs(num + 1, max(last, vp[i].a) + vp[i].c)) return true;
check[i] = false;
}
}
return false;// 全部都遍历, 如果没有满足情况的话, 说明不行, 就返回
}
int main()
{
cin >> T; // 组数
for(int i = 0; i < T; i++)
{
cin >> N;
for(int i = 0; i < N; i++)
{
cin >> vp[i].a >> vp[i].b >> vp[i].c;
}
if(dfs(0, 0)) cout << "YES" << endl;
else cout << "NO" << endl;
}
return 0;
}
dfs 迷宫搜索类问题
搜索类问题一般都是在二维矩阵中进行,以某个坐标为起点进行一次/多次深度优先遍历,直到遇到某个位置停止。
在二维矩阵中搜索,一般可以定义两个向量数组
// 当题目要求只能上下左右四个方向走时
int dx[4] = { 0, 0, 1, -1 };
int dy[4] = { 1, -1, 0, 0 };
// 如当前的位置坐标为 (i, j), 那么可以走的位置有:(矩阵 m 行 n 列)
for(int k = 0; k < 4; k++)
{
int x = dx[k] + i, int y = dy[k] + j;
if(x >= 0 && x < m && y >= 0 && y < n && !visit[x][y] && /\* 题目要求条件\*/)
{
visit[x][y] = true;
dfs(x, y, /\*\*/);
}
}
// 当题目要求可以上下左右、斜左上、斜右上、斜左下、斜右下这8个方向走时
int dx[4] = { 0, 0, 1, -1 , 1, 1, -1, -1};
int dy[4] = { 1, -1, 0, 0 , -1, 1, -1, 1};
for(int k = 0; k < 8; k++)
{
int x = dx[k] + i, int y = dy[k] + j;
if(x >= 0 && x < m && y >= 0 && y < n && !visit[x][y] && /\* 题目要求条件\*/)
{
![img](https://img-blog.csdnimg.cn/img_convert/067afcb284aed4d24402b4c2cf74c20a.png)
![img](https://img-blog.csdnimg.cn/img_convert/2b83630e7ad99c2f6253f0179b4cbdd7.png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
g-17N42p8V-1715565385134)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**