该文章长期更新
文章目录
1、Dijkstra
介绍:求源点到其他所有点的最短路径
实例模板:
函数使用介绍:
第一个参数:为所有边权集合:eg:times = [[2,1,1],[2,3,1],[3,4,1]] ,解释【2,1,1】表示2到1点的距离为1,其他类似
第二个参数:表示有多少个节点
第三个参数表源点是第几个节点
函数返回一个距离数组,如dist[i]表源节点到i点的最短距离。
使用注意事项:函数返回的dist数组,下表0位置未使用,因此从1到n才是有意义的值
vector<int> Dijkstra(vector<vector<int>>& times, int n, int k)
{
int inf = INT_MAX / 2;
vector<vector<int>> map(n + 1, vector<int>(n + 1, inf));
//初始化map邻接矩阵
for (auto& item : times)
{
map[item[0]][item[1]] = item[2];
}
// 定义一个距离数组,用来表示源节点到该节点的距离
vector<int> dist(n + 1, inf);
//初始化源节点距离为0;
dist[k] = 0;
//定义一个标记数组,判断当前点是否被标记,长度为n+1
vector<bool> used(n + 1, false);
//寻找一个最短边
for (int i = 1; i <= n; i++)
{
int x = -1;
for (int j = 1; j <= n; j++)
{
if (!used[j] && (x == -1 || dist[j] < dist[x]))
x = j;
}
//标记该店已使用
used[x] = true;
//寻找到最短边之后,使用该点开始更新dist数组
for (int j = 1; j <= n; j++)
{
dist[j] = min(dist[j], dist[x] + map[x][j]);
}
}
return dist;
}
2、冒泡排序
每一次排序,寻找一个最大值放在最后面,然后下一次进行的轮数减1;
函数使用方法:
第一个参数为数组的首指针
第二个参数为数组的长度
函数不需要返回,因为使用的是指针,因此直接修改了原数组的排序
void maopao(int* arr, int n)
{
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int tem = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tem;
}
}
}
}
3、获取文件目录下所有文件
3.1、linux中
//头文件
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <vector>
#include <string>
#include<iostream>
#include<algorithm>
#include <string.h>
函数接口:
使用方法:
第一个参数为要遍历的文件路径
第二个参数为一个string的vector向量,用来存放遍历的文件路径
第三个参数为指定遍历的文件后缀,不指定表示遍历所有的文件
//从指定路径获取文件路径;不指定后缀suffixs则获取所有文件路径
int getFiles(const string& path, vector<string> &files, vector<string> suffixs = {})
{
int FileCnt = 0;
DIR *dirp;
struct dirent *dp;
dirp = opendir(path.c_str());
if (dirp == NULL) {
printf("opendir %s failed\n",path.c_str());
return 0;
}
while ((dp = readdir(dirp)) != NULL) {
string curpath(path);
if (path.back() != '/') {
curpath += string("/") += dp->d_name;
} else {
curpath += dp->d_name;
}
//如果是目录,递归查找
if(dp->d_type == DT_DIR) {
if(0 != strcmp(dp->d_name,".") && 0 != strcmp(dp->d_name,"..")){
FileCnt += getFiles(curpath, files, suffixs);
}
}
//判断是否为文件以及文件后缀名
else if (dp->d_type == DT_REG) {
if (suffixs.size() <=0 ) {
files.push_back(curpath);
FileCnt++;
} else {
for (auto suffix : suffixs) {
if (string(strrchr(dp->d_name,'.'))==suffix) {
files.push_back(curpath);
FileCnt++;
break;
}
}
}
}
}
closedir(dirp);
return FileCnt;
}
3.2、win中
//头文件
#include <string>
#include<io.h>
#include <fstream>
#include <iostream>
函数接口:
使用方法:
第一个参数为要遍历的文件路径
第二个参数为一个string的vector向量,用来存放遍历的文件路径
void getFiles(string path, vector<string>& files)
{
//文件句柄
long long hFile = 0;
//文件信息,声明一个存储文件信息的结构体
struct _finddata_t fileinfo;
string p;//字符串,存放路径
if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)//若查找成功,则进入
{
do
{
//如果是目录,迭代之(即文件夹内还有文件夹)
if ((fileinfo.attrib & _A_SUBDIR))
{
//文件名不等于"."&&文件名不等于".."
//.表示当前目录
//..表示当前目录的父目录
//判断时,两者都要忽略,不然就无限递归跳不出去了!
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
getFiles(p.assign(path).append("\\").append(fileinfo.name), files);
}
//如果不是,加入列表
else
{
files.push_back(p.assign(path).append("\\").append(fileinfo.name));
}
} while (_findnext(hFile, &fileinfo) == 0);
//_findclose函数结束查找
_findclose(hFile);
}
}
4、字符串切割
//引用头文件
#include <string>
#include<io.h>
#include <fstream>
#include <iostream>
函数接口:
使用方法:
第一个参数为要进行切割的字符串
第二个参数为用来存放切割字符串的string向量
第三个参数为要使用什么符号进行切割
EG:
SplitString(label_tem, name_tem, "\\");
void SplitString(const string& s, vector<string>& v, const string& c)
{
string::size_type pos1, pos2;
pos2 = s.find(c);
pos1 = 0;
while (string::npos != pos2)
{
v.push_back(s.substr(pos1, pos2 - pos1));
pos1 = pos2 + c.size();
pos2 = s.find(c, pos1);
}
if (pos1 != s.length())
v.push_back(s.substr(pos1));
}
5、寻找一个字符串中回文字符串
思路,从中间向两边进行发散:
下面摘抄leetcode上面的一个讲解图:
自己代码实现函数接口:
使用方法:
直接输入一个字符串,函数返回字串中最长的回文子串;
string The_longest_palindrome(string s) // 最大回文子串
{
// 思路,从中间向两边进行发散
string res="";
for (int i = 0; i < s.length(); i++)
{
if (i - 1 < 0||i+1>s.length())
{
res = s[i];
}
else
{
int flag = 0;
int start=0, end = 0;
while (i-flag>=0&&i+flag<s.length()&& s[i-flag] == s[i+flag])// 奇数判断
{
start = i - flag;
end = i + flag;
flag++;
}
int flag1 = 0;
int start1 = 0, end1 = 0;
while (i - 1 - flag1>=0 && s[i - 1 - flag1] == s[i + flag1]) //偶数判断
{
start1 = i - 1 - flag1;
end1 = i + flag1;
flag1++;
}
if (end - start+1 > res.length())
{
res = s.substr(start,end-start+1);
}
if (end1 - start1+1 > res.length())
{
res = s.substr(start1, end1 - start1 + 1);
}
}
}
return res;
}
函数接口2:将所有字符串全部变为奇数,即在每个字符中间都插入一个特殊字符,然后进行中心扩散:
使用方法:和上面一样。
string The_longest_palindrome(string s) // 最大回文子串
{
// 因为字串可能为奇数也可能为偶数,因此在进行中间扩散的时候会造成重复,因此这里在字符中间插入特殊符号,使其全部变为奇数
string tem;
for (int i = 0; i <s.length(); i++)
{
tem.append("\r");
tem.append(&s[i],1); // 因为这里使用了字符串的地址,而字符串地址代表整个字符串,因此需要使用1囧限制
if(i== s.length()-1)
tem.append("\r");
}
/*cout << tem << endl;
system("pause");*/
// 思路,从中间向两边进行发散
string res="";
for (int i = 0; i < tem.length(); i++)
{
if (i - 1 < 0||i+1> tem.length())
{
res = tem[i];
}
else
{
int flag = 0;
int start=0, end = 0;
while (i-flag>=0&&i+flag< tem.length()&& tem[i-flag] == tem[i+flag])// 奇数判断
{
start = i - flag;
end = i + flag;
flag++;
}
if (end - start+1 > res.length())
{
res = tem.substr(start,end-start+1);
}
}
}
// 进行数据解析,去掉特殊字符
string r;
for (int i = 0; i < res.length(); i++)
{
if (res[i] == '\r')
continue;
r.append(&res[i], 1);
}
return r;
}
6、c++动态申请二维数组
6.1、c语言风格(malloc)
可使用下面代码生成一个动态的二维数组。
int m, n;//m行n列
cin >> m >> n; // 接收m和n
int**map = (int **)malloc(m*sizeof(int *)); // 建立一个二维数组指针,并进行m行内存申请
for (int i = 0; i < m; i++) // 对每行的元素进行内存申请
map[i] = (int*)malloc(n* sizeof(int)); // 进行内存申请
// 进行初始化
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
memset(map[i],0,n*sizeof(int));
将其封装成一个函数:
使用方式:直接定义一个二维指针变量,然后输入多少行,多少列,即可返回这个二维数组的指针。
eg:
int** map = to_arr(m,n);
int** to_arr(int a, int b)
{
int** map = (int**)malloc(a * sizeof(int*));
for (int i = 0; i < a; i++)
map[i] = (int*)malloc(b * sizeof(int)); // 进行内存申请
// 进行初始化
for (int i = 0; i < a; i++)
for (int j = 0; j < b; j++)
memset(map[i],0, b * sizeof(int));
return map;
}
6.2、c++风格(new)
定义一个函数模板:
template<typename R>
R** malloc_2arr(int m, int n)
{
R** _m = new R*[m];
for (int i = 0; i < m; i++)
_m[i] = new R[n];
return _m;
}
使用方式,要创建什么类型直接在创建的时候进行指定即可
示例如下:
string** map = malloc_2arr<string>(m,n);
for(int i=0;i<m;i++)
for (int j = 0; j < n; j++)
{
cin >> map[i][j];
}
但是在使用之后,一定要记得对对象指针进行释放。
for (int i = 0; i < m; i++)
delete[]_m[i];
delete[]_m;
上面的方式需要进行手动进行释放内存,非常不方便,因此,我们将其封装成一个类模板,让操作系统来其进行释放。
template<class R>
class arr
{
public:
arr(int m,int n)
{
this->m = m;
this->n = n;
}
R** malloc_2arr()
{
_m = new R * [m];
for (int i = 0; i < m; i++)
_m[i] = new R[n];
return _m;
}
~arr()
{
cout << "析构函数运行" << endl;
for (int i = 0; i < m; i++)
delete[]_m[i];
delete[]_m;
}
使用方式
arr<string> tem(m,n); // 定义一个对象,m,n代表地图的m行和n列
string** map = tem.malloc_2arr(); // 进行内存申请
7、DFS迷宫问题
7.1、寻找最小的路径步数
函数接口
使用方式:给定起始坐标x,y,以及终点坐标dxs,dxy,以及整个地图的二维指针变量map,m,n代表含有多少行和多少列。step为起始步数。
eg:
dfs(0,0,m-1,n-1,map,m,n,0);//这里要注意的是终点坐标需要在(m-1,n-1)之间。
void dfs(int x,int y,int dxs,int dxy,int **map,int m,int n,int step)
{
// 函数的出口
if (x == dxs && y == dxy)
{
// 因为是求最短路径,因此我们需要和step进行比较
if (step < mi)
mi = step;
return;
}
// 进行是个方向进行探索
for (int i = 0; i < 4; i++)
{
int tx = x + dx[i];
int ty = y + dy[i];
// 判断是否越界,并且判断是否没有访问
if (tx >= 0 && ty >= 0 && tx < m && ty < n && !vis[tx][ty]) // 注意这里tx是小于行,ty是小于列
{
// 如果没有走过,并且没有越界,进行探索
// 首先标记这个点为已经走过
vis[tx][ty] = 1;
dfs(tx, ty, dxs, dxy, map, m, n,step+1);
// 回溯的时候将其取消标记
vis[tx][ty] = 0;
}
}
}
使用案例:
#include<bits/stdc++.h>
using namespace std;
// 定义dfs的四个方向
int dx[4] = { 0,1,0,-1 }; //顺序为:上右下左
int dy[4] = { -1,0,1,0 }; //顺序为:上右下左
// 定义一个标记访问数组
int** vis = NULL;
int mi = 99999;
void dfs(int x,int y,int dxs,int dxy,int **map,int m,int n,int step)
{
// 函数的出口
if (x == dxs && y == dxy)
{
// 因为是求最短路径,因此我们需要和step进行比较
if (step < mi)
mi = step;
return;
}
// 进行是个方向进行探索
for (int i = 0; i < 4; i++)
{
int tx = x + dx[i];
int ty = y + dy[i];
// 判断是否越界,并且判断是否没有访问
if (tx >= 0 && ty >= 0 && tx < m && ty < n && !vis[tx][ty]) // 注意这里tx是小于行,ty是小于列
{
// 如果没有走过,并且没有越界,进行探索
// 首先标记这个点为已经走过
vis[tx][ty] = 1;
dfs(tx, ty, dxs, dxy, map, m, n,step+1);
// 回溯的时候将其取消标记
vis[tx][ty] = 0;
}
}
}
int** to_arr(int a, int b)
{
int** map = (int**)malloc(a * sizeof(int*));
for (int i = 0; i < a; i++)
map[i] = (int*)malloc(b * sizeof(int)); // 进行内存申请
// 进行初始化
for (int i = 0; i < a; i++)
for (int j = 0; j < b; j++)
memset(map[i],0, b * sizeof(int));
return map;
}
int main() {
int m, n;
cin >> m >> n;
int** map = to_arr(m,n); // 申请二维动态数组
vis = to_arr(m, n);//申请已访问标记数组
//开始接受输入数据
for (int i = 0; i < m; i++) // 进行数据的读入
for (int j = 0; j < n; j++)
cin>>map[i][j];
// 假设几点为(0,0),总点为(m-1,n-1)
// 开始进行dfs
//将0,0标记为已访问
vis[0][0] = 1;
dfs(0,0,m-1,n-1,map,m,n,0);
cout << mi << endl;
return 0;
}
测试用例“
4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
7.2、记录最小路径的路径
函数封装接口:
使用方式:输入起始坐标x,y,终点坐标dxs,dxy,地图map的指针,以及多少行m,多少列n,以及其实步数。
eg:
dfs(0,0,m-1,n-1,map,m,n,0);
void dfs(int x,int y,int dxs,int dxy,int **map,int m,int n,int step)
{
// 函数的出口
if (x == dxs && y == dxy)
{
// 统计这一条路径的中总和路径长度
int tem=0;
for (int i = 0; i < path.size(); i++)
{
tem += map[path[i].first][path[i].second];
}
// 如果这条路径比前一条还短,那么就进行路径更新
if (tem < mi)
{
min_path = path;
mi = tem;
}
return;
}
// 进行是个方向进行探索
for (int i = 0; i < 4; i++)
{
int tx = x + dx[i];
int ty = y + dy[i];
// 判断是否越界,并且判断是否没有访问
if (tx >= 0 && ty >= 0 && tx < m && ty < n && !vis[tx][ty]) // 注意这里tx是小于行,ty是小于列
{
// 如果没有走过,并且没有越界,进行探索
// 首先标记这个点为已经走过
vis[tx][ty] = 1;
// 并将这个点压入path中
path.push_back(pair<int,int>(tx,ty));
dfs(tx, ty, dxs, dxy, map, m, n,step+1);
// 回溯的时候将其取消标记
vis[tx][ty] = 0;
// 并将这个点出站
path.pop_back();
}
}
}
使用案例:
#include<bits/stdc++.h>
using namespace std;
// 定义dfs的四个方向
int dx[4] = { 0,1,0,-1 }; //顺序为:上右下左
int dy[4] = { -1,0,1,0 }; //顺序为:上右下左
// 定义一个标记访问数组
int** vis = NULL;
int mi = 99999; //最小的路径长度
vector<pair<int, int>> path; // 代表走过的路径的坐标
vector<pair<int, int>> min_path; //记录最小的路径
void dfs(int x,int y,int dxs,int dxy,int **map,int m,int n,int step)
{
// 函数的出口
if (x == dxs && y == dxy)
{
// 统计这一条路径的中总和路径长度
int tem=0;
for (int i = 0; i < path.size(); i++)
{
tem += map[path[i].first][path[i].second];
}
// 如果这条路径比前一条还短,那么就进行路径更新
if (tem < mi)
{
min_path = path;
mi = tem;
}
return;
}
// 进行是个方向进行探索
for (int i = 0; i < 4; i++)
{
int tx = x + dx[i];
int ty = y + dy[i];
// 判断是否越界,并且判断是否没有访问
if (tx >= 0 && ty >= 0 && tx < m && ty < n && !vis[tx][ty]) // 注意这里tx是小于行,ty是小于列
{
// 如果没有走过,并且没有越界,进行探索
// 首先标记这个点为已经走过
vis[tx][ty] = 1;
// 并将这个点压入path中
path.push_back(pair<int,int>(tx,ty));
dfs(tx, ty, dxs, dxy, map, m, n,step+1);
// 回溯的时候将其取消标记
vis[tx][ty] = 0;
// 并将这个点出站
path.pop_back();
}
}
}
int** to_arr(int a, int b)
{
int** map = (int**)malloc(a * sizeof(int*));
for (int i = 0; i < a; i++)
map[i] = (int*)malloc(b * sizeof(int)); // 进行内存申请
// 进行初始化
for (int i = 0; i < a; i++)
for (int j = 0; j < b; j++)
memset(map[i],0, b * sizeof(int));
return map;
}
int main() {
int m, n;
cin >> m >> n;
int** map = to_arr(m,n); // 申请二维动态数组
vis = to_arr(m, n);//申请已访问标记数组
//开始接受输入数据
for (int i = 0; i < m; i++) // 进行数据的读入
for (int j = 0; j < n; j++)
cin>>map[i][j];
// 假设几点为(0,0),总点为(m-1,n-1)
// 开始进行dfs
//将0,0标记为已访问
vis[0][0] = 1;
// 首先将原点压入path中。
path.push_back(pair<int,int>(0,0));
dfs(0,0,m-1,n-1,map,m,n,0);
// 输出最短路径以及其周国的路径
for (int i = 0; i < min_path.size(); i++)
{
cout << map[min_path[i].first][min_path[i].second] << " ";
}
cout << endl;
cout <<"最小路径长度为:"<<mi << endl;
return 0;
}
8、DFS选数问题
从一个给定的数组中,选择出满足条件的数据的组合。
实例模板:
使用方式:
使用该函数接口时,需要首先定义几个变量:
int a[4] = { 1, 3, 6 ,6 }; // 这个数组表示需要进行选择的数组
int n = 4; // 一共有多少个数
int max_re = -999; // 用来存放和的最大值
vector<int> res; // 用来存放临时选的数据
vector<vector<int>> result; // 用来存放最后输出数的结果
函数主体:
void dfs(int cur, int cnt, int num)
{
// 第一个参数表示当前已经选到那个位置了
// 第二个参数表示当前已经选择了多少个了
// 第三个表示当前选择的数的和
if (cur == n - 1)//当前坐标已经到达了最后一个
{
if ((num % 7 == 0) && (num > max_re))
{
//ans++;
if (result.size() != 0)
result.pop_back();
max_re = num;
result.push_back(res);
}
return;
}
dfs(cur + 1, cnt, num);//不拿
res.push_back(a[cur + 1]);
dfs(cur + 1, cnt + 1, num + a[cur + 1]);//拿
res.pop_back();
}
使用示例:
从数组中选择满足和为7的倍数,并且和是最大的一组数据。
#include<bits/stdc++.h>
using namespace std;
int a[4] = { 1, 3, 6 ,6 }; // 这个数组表示需要进行选择的数组
int n = 4; // 一共有多少个数
int max_re = -999; // 用来存放和的最大值
vector<int> res; // 用来存放临时选的数据
vector<vector<int>> result; // 用来存放最后输出数的结果
void dfs(int cur, int cnt, int num)
{
// 第一个参数表示当前已经选到那个位置了
// 第二个参数表示当前已经选择了多少个了
// 第三个表示当前选择的数的和
if (cur == n - 1)//当前坐标已经到达了最后一个
{
if ((num % 7 == 0) && (num > max_re))
{
//ans++;
if (result.size() != 0)
result.pop_back();
max_re = num;
result.push_back(res);
}
return;
}
dfs(cur + 1, cnt, num);//不拿
res.push_back(a[cur + 1]);
dfs(cur + 1, cnt + 1, num + a[cur + 1]);//拿
res.pop_back();
}
int main() {
dfs(-1, 0, 0); // 注意要从-1开始
for (int i = 0; i < result.size(); i++)
{
for (int j = 0; j < result[i].size(); j++)
cout << result[i][j] << " ";
cout << endl;
}
cout << "max_re:" << max_re << endl;
return 0;
}
输出:
9、递归选择不同电话号码组合
题目来源:leetcode:17. 电话号码的字母组合
题目描述:
解决方法:进行每一层递归选择。
实现代码:
#include<iostream>
#include<map>
#include<vector>
using namespace std;
// 定义一个vector用来装结果
vector<string> result;
string dfs_path="";
map<int, string> all;
// 进行递归搜索答案
void dfs(string path,string digits)
{
if (digits.size() == 0)
return;
if (path.size() == digits.size()) // 是否选到了最后一个
{
// 将这个结果存起来
result.push_back(path);
return;
}
int curDig = digits[path.size()] - '0';
// 获取这个数字对应的字符串
string s = all[curDig];
// 对字符串s中每个字母进行向下搜索
for (int i = 0; i < s.size(); i++)
{
//首选将这个字符加入已选路径
path += s[i];
dfs(path,digits);
// 将这个字符去掉
path = path.substr(0,path.size()-1);
}
}
int main()
{
string digits = "";
cin >> digits;
// 创建一个映射
all[2] = "abc"; all[3] = "def";
all[4] = "ghi"; all[5] = "jkl"; all[6] = "mno";
all[7] = "pqrs"; all[8] = "tuv"; all[9] = "wxyz";
dfs(dfs_path,digits);
for (int i = 0; i < result.size(); i++)
{
cout << result[i] << endl;
}
return 0;
}
10、c++中vector删除元素
函数接口:
使用方法:
直接将要删除的vector放入函数,后面tar为要删除的值:
eg:
vector<int> all;
int tar = 5;
dele_vector(all,tar);
void dele_vector(vector<int> &a,int tar)
{
// 进行重组
for (vector<int>::iterator it = a.begin(); it != a.end(); )
{
if (*it == tar)
{
it = a.erase(it); //不能写成arr.erase(it);
}
else
{
++it;
}
}
}
11、使用递归翻转stack堆栈元素
主要看考文章:链接
以下为根据自己的理解进行自己实现:
实现思路:
首选我们需要每次都对原始stack进行操作,每次获取原始stack中的最后一个元素,并将其从stack中进行移除。当拿到这个值之后 ,我们在进行递归,因为这里每次移除的都是最后一个元素,因此在递归的深处其实也就是stack的头元素,此时,我们只需要对其进行逐个入栈即可:
获取和移除最后一个元素的函数
int get_remove_last(stack<int>& s) // 首先需要保证这个s不为空
{
int tem = s.top();
s.pop();
if (s.empty()) return tem;
int last = get_remove_last(s);
s.push(tem);
return last;
}
进行递归翻转的函数
// 这个函数用来进行翻转
void rever(stack<int>& s)
{
if (s.empty()) return; // 这里首先进行空的判断
// 首选获取最后一个元素,并将最后一个元素移除
int last = get_remove_last(s);
rever(s);
s.push(last);
}
使用示例:
#include<iostream>
#include<string>
#include<set>
#include<algorithm>
#include<vector>
#include<unordered_set>
#include <memory>
#include<map>
#include<stack>
#include"Utils.h"
using namespace std;
int get_remove_last(stack<int>& s) // 首先需要保证这个s不为空
{
int tem = s.top();
s.pop();
if (s.empty()) return tem;
int last = get_remove_last(s);
s.push(tem);
return last;
}
// 这个函数用来进行翻转
void rever(stack<int>& s)
{
if (s.empty()) return; // 这里首先进行空的判断
// 首选获取最后一个元素,并将最后一个元素移除
int last = get_remove_last(s);
rever(s);
s.push(last);
}
int main() {
stack<int> s;
for (int i = 1; i <= 5; i++)
s.push(i);
rever(s);
while (!s.empty())
{
cout << s.top() << " ";
s.pop();
}
cout << endl;
return 0;
}
12、c++在函数中申请高纬数组的方法
12.1、申请一维数组
// c++ 在函数创建一维数组
int* createOneArray(int m)
{
int* arr = new int[m];
return arr;
}
12.2、申请二维数组
// c++ 在函数创建二维数组
int** createTwoArray(int m, int n)
{
int** arr = new int* [m];
for (int i = 0; i < m; i++)
arr[i] = new int[n];
return arr;
}
12.3、申请三维数组
// c++ 在函数创建三维数组
int*** createThreeArray(int m, int n,int k)
{
int*** arr = new int** [m];
for (int i = 0; i < m; i++)// 进行初始化
{
arr[i] = new int* [n];
for (int j = 0; j < n; j++)
{
arr[i][j] = new int[k];
}
}
return arr;
}
12.4、申请其他更高纬数组
依次类推:
12.5、使用范例
包含上面数组的使用范例:
#include<iostream>
#include<string>
#include<set>
#include<algorithm>
#include<vector>
#include<unordered_set>
#include <memory>
#include<map>
#include<stack>
//#include"Utils.h"
using namespace std;
// c++ 在函数创建一维数组
int* createOneArray(int m)
{
int* arr = new int[m];
return arr;
}
// c++ 在函数创建二维数组
int** createTwoArray(int m, int n)
{
int** arr = new int* [m];
for (int i = 0; i < m; i++)
arr[i] = new int[n];
return arr;
}
// c++ 在函数创建三维数组
int*** createThreeArray(int m, int n,int k)
{
int*** arr = new int** [m];
for (int i = 0; i < m; i++)// 进行初始化
{
arr[i] = new int* [n];
for (int j = 0; j < n; j++)
{
arr[i][j] = new int[k];
}
}
return arr;
}
int main() {
cout << "******* 一维数组创建 *******" << endl;
int* arr1 = createOneArray(10);
for (int i = 0; i < 10; i++)
{
arr1[i] = i;
}
for (int i = 0; i < 10; i++)
{
cout << arr1[i] << " ";
}
cout << endl;
cout << "******* 二维数组创建 *******" << endl;
int** arr2 = createTwoArray(2,3);
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
arr2[i][j] = i + j;
}
}
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
//arr[i][j] = i + j;
cout << arr2[i][j] << " ";
}
cout << endl;
}
cout << "******* 三维数组创建 *******" << endl;
int*** arr3 = createThreeArray(2, 3,2);
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
for (int k = 0; k < 2; k++)
{
arr3[i][j][k] = i + j + k;
}
}
}
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
for (int k = 0; k < 2; k++)
{
//arr[i][j][k] = i + j + k;
cout << arr3[i][j][k] << " ";
}
cout << endl;
}
cout << endl;
}
return 0;
}
13、dfs判断矩阵块数问题
{1,0,0,0},
{0,1,0,0},
{1,1,1,0},
{0,1,0,1}
在上面这个矩阵中,我们要判断1连通的块数,1连通表示只要和1进行上下左右可以到达并且进行连通那么-就认为他们在同一块中,如果不能到达,那么就不属于一块,如上数据中,1连通的块数就是3.
解题思路:
使用dfs进行四边衍生,直到不能延伸为止。但这个需要注意的是,和普通的回溯算法不一样,在这个题目中,我们只需要进行四个方向机芯探寻,当到达一个地方之后,就将这个地图中的点置位0,而不能进行回溯将其在置位1,否则将无限陷入死循环。
实现代码:
// c++的设计模式----观察者模式
#include<iostream>
#include<memory>
#include<deque>
#include<list>
#include<string>
#include <algorithm>
#include<vector>
#include<unordered_map>
using namespace std;
class Solution {
public:
int res = 0;
int di[4] = {-1,0,1,0};
int dj[4] = {0,1,0,-1};
int getKui(vector<vector<int> > &array) {
for(int i=0;i<array.size();i++)
{
for(int j=0;j<array[i].size();j++)
{
if(array[i][j]==1)
{
res++;
dfs(array,i,j);
}
}
}
return res;
}
void dfs(vector<vector<int> > &array,int i,int j)
{
// 首先将该位置进行消除
array[i][j] = 0;
// 进行上右下左四个方向进行遍历
for(int k=0;k<4;k++)
{
int dx = i+di[k];
int dy = j+dj[k];
// 判断是否超过界限,并且是否还可以进行伸缩
if(dx>=0&&dx<array.size()&&dy>=0&&dy<array[0].size()&&array[dx][dy]==1)
{
// 如果可以,进行伸缩
dfs(array,dx,dy); // 注意这里不需要进行回溯,否则就会陷入死循环
}
}
}
};
int main()
{
vector<vector<int> > arr = {
{1,0,0,0},
{0,1,0,0},
{1,1,1,0},
{0,1,0,1}
};
Solution p;
auto res = p.getKui(arr);
cout<<res<<endl;
return 0;
}
相识题目视频教程:蓝桥杯算法很美-水洼数目
14、dp选取数组中和最大的连续数组
牛客链接
思路:
既然是连续子数组,如果我们拿到了当前的和,对于后面一个即将加入的元素,如果加上他这一串会变得更大,我们肯定会加上它,如果它自己会比加上前面这一串更大,说明从它自己开始连续子数组的和可能会更大。
那我们可以用dp数组表示以下标i为终点的最大连续子数组和,则每次遇到一个新的数组元素,连续的子数组要么加上变得更大,要么它本身就更大,因此状态转移为dp[i]=max(dp[i−1]+array[i],array[i])dp[i] = max(dp[i - 1] + array[i], array[i])dp[i]=max(dp[i−1]+array[i],array[i]),这是最基本的求连续子数组的最大和。
但是题目要求需要返回长度最长的一个,我们则每次用left、right记录该子数组的起始,需要更新最大值的时候(要么子数组和更大,要么子数组和相等的情况下区间要更长)顺便更新最终的区间首尾,这样我们的区间长度就是最长的。
具体做法:
- step 1:创建动态规划辅助数组,记录到下标i为止的最大连续子数组和,下标为0的时候,肯定等于原数组下标为0的元素。
- step 2:准备左右区间双指针记录每次连续子数组的首尾,再准备两个双指针记录最大和且区间最长的连续子数组的首尾。
- step 3:遍历数组,对于每个元素用上述状态转移公式记录其dp值,更新区间首尾(如果需要)。
- step 4:出现一个最大值。且区间长度更大的时候,更新记录最长区间的双指针。
- step 5:根据记录的最长子数组的位置取数组。
完整cpp代码:
#include<iostream>
#include<memory>
#include<deque>
#include<list>
#include<string>
#include <algorithm>
#include<vector>
#include<unordered_map>
using namespace std;
void printOneVector(vector<int> &v)
{
for( const auto &item:v)
{
cout<<item<<" ";
}
cout<<endl;
}
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param array int整型vector
* @return int整型vector
*/
vector<int> FindGreatestSumOfSubArray(vector<int>& array) {
// write code here
// 使用动态规划进行搜索
vector<int> dp(array.size(),0);
dp[0] = array[0];
// 定义一个全局变量,用以记录递推的时候的最大值
int mx = dp[0];
// 定义一个滑动区间
int lef = 0;int rig = 0;
// 定义一个最大最长的区间
int relef=0;int rerig = 0;
for(int i=1;i<array.size();i++)
{
// 滑动区间加1
rig++;
if(dp[i-1]+array[i]<array[i])
{
// 如果前面区间加上当前值小于当前值,那么滑动窗口应该从当前位置从新开始
lef=rig;
}
// 进行状态转移
dp[i] = max(dp[i-1]+array[i],array[i]);
// 更新最大值的结果区间
if(dp[i]>=mx&&((rig-lef+1)>(rerig-relef+1)))
{
// 如果当前的区间更大,且更长,那么更新结果的左右区间
mx = dp[i];
rerig = rig;
relef = lef;
}
}
// 将结果存放到res中进行返回
// vector<int> res;
return vector<int>(array.begin()+relef,array.begin()+rerig+1);
}
};
int main()
{
vector<int> arr={-1,-2,-3,-4,5};
vector<int> res = Solution().FindGreatestSumOfSubArray(arr);
printOneVector(res);
return 0;
}
15、c++中读取一行数据为string类型
方法:getline(cin,string);
作用:读取一行数据到string 对象中。
使用案例:
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
int main() {
string s;
getline(cin, s);
cout << s << endl;
return 0;
}
输出:输入的一行数据
16、c++常用排序算法
16.1、归并排序
1、分离函数
void mergesort(vector<int>& arr, int x, int y) //分离,x 和 y 分别代表要分离数列的开头和结尾
{
if (x >= y) return; //如果开头 ≥ 结尾,那么就说明数列分完了,就要返回
int mid = (x + y) / 2; //将中间数求出来,用中间数把数列分成两段
mergesort(arr, x, mid);
mergesort(arr, mid + 1, y); //递归,继续分离
merge(arr, x, mid, y); //分离玩之后就合并
}
2、合并函数
void merge(vector<int>& arr, int low, int mid, int high)
//low 和 mid 分别是要合并的第一个数列的开头和结尾,mid+1 和 high 分别是第二个数列的开头和结尾
{
// 定义一个缓存区间
vector<int> tem(arr.size(), 0);
int i = low, j = mid + 1, k = low;
//i、j 分别标记第一和第二个数列的当前位置,k 是标记当前要放到整体的哪一个位置
while (i <= mid && j <= high) //如果两个数列的数都没放完,循环
{
if (arr[i] < arr[j])
tem[k++] = arr[i++];
else
tem[k++] = arr[j++]; //将a[i] 和 a[j] 中小的那个放入 b[k],然后将相应的标记变量增加
} // b[k++]=a[i++] 和 b[k++]=a[j++] 是先赋值,再增加
while (i <= mid)
tem[k++] = arr[i++];
while (j <= high)
tem[k++] = arr[j++]; //当有一个数列放完了,就将另一个数列剩下的数按顺序放好
for (int i = low; i <= high; i++)
arr[i] = tem[i]; //将 b 数组里的东西放入 a 数组,因为 b 数组还可能要继续使用
}
完整代码:
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include<vector>
//#include"Utils.h"
using namespace std;
//int n, a[12000], b[12000];
void merge(vector<int>& arr, int low, int mid, int high)
//low 和 mid 分别是要合并的第一个数列的开头和结尾,mid+1 和 high 分别是第二个数列的开头和结尾
{
// 定义一个缓存区间
vector<int> tem(arr.size(), 0);
int i = low, j = mid + 1, k = low;
//i、j 分别标记第一和第二个数列的当前位置,k 是标记当前要放到整体的哪一个位置
while (i <= mid && j <= high) //如果两个数列的数都没放完,循环
{
if (arr[i] < arr[j])
tem[k++] = arr[i++];
else
tem[k++] = arr[j++]; //将a[i] 和 a[j] 中小的那个放入 b[k],然后将相应的标记变量增加
} // b[k++]=a[i++] 和 b[k++]=a[j++] 是先赋值,再增加
while (i <= mid)
tem[k++] = arr[i++];
while (j <= high)
tem[k++] = arr[j++]; //当有一个数列放完了,就将另一个数列剩下的数按顺序放好
for (int i = low; i <= high; i++)
arr[i] = tem[i]; //将 b 数组里的东西放入 a 数组,因为 b 数组还可能要继续使用
}
void mergesort(vector<int>& arr, int x, int y) //分离,x 和 y 分别代表要分离数列的开头和结尾
{
if (x >= y) return; //如果开头 ≥ 结尾,那么就说明数列分完了,就要返回
int mid = (x + y) / 2; //将中间数求出来,用中间数把数列分成两段
mergesort(arr, x, mid);
mergesort(arr, mid + 1, y); //递归,继续分离
merge(arr, x, mid, y); //分离玩之后就合并
}
int main()
{
vector<int> arr;
for (int i = 0; i < 10; i++)
{
int tem; cin >> tem;
arr.push_back(tem);
}
mergesort(arr, 0, arr.size() - 1);
for (int i = 0; i < arr.size(); i++)
{
cout << arr[i] << endl;
}
}
16.2、快排
//
// array 为待排的数组
// low 为起始,为0
// high 为长度,从0开始计算,为array.size()-1
void QuickSort(vector<int> &array, int low, int high) { //快排
if (low >= high) { //若待排序序列只有一个元素,返回空
return;
}
int i = low; //i作为指针从左向右扫描
int j = high; //j作为指针从右向左扫描
int key = array[low];//第一个数作为基准数
while (i < j) {
while (array[j] >= key && i < j) { //从右边找小于基准数的元素 (此处由于j值可能会变,所以仍需判断i是否小于j)
j--; //找不到则j减一
}
array[i] = array[j]; //找到则赋值
while (array[i] <= key && i < j) { //从左边找大于基准数的元素
i++; //找不到则i加一
}
array[j] = array[i]; //找到则赋值
}
array[i] = key; //当i和j相遇,将基准元素赋值到指针i处
QuickSort(array, low, i - 1); //i左边的序列继续递归调用快排
QuickSort(array, i + 1, high); //i右边的序列继续递归调用快排
}
16.3、冒泡排序
每一次排序,寻找一个最大值放在最后面,然后下一次进行的轮数减1;
函数使用方法:
第一个参数为数组的首指针
第二个参数为数组的长度
函数不需要返回,因为使用的是指针,因此直接修改了原数组的排序
void maopao(int* arr, int n)
{
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int tem = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tem;
}
}
}
}
17、并查集
参考链接
cpp代码:
#include<iostream>
#include<vector>
using namespace std;
int find(int x,vector<int> &arr) // 查询本节点的父节点
{
if (arr[x] == x)
{
return x;
}
else
{
// 为防止每次递归都要进行深层次遍历,这里进行路径压缩
arr[x] = find(arr[x],arr);
return arr[x]; // 返回父节点
}
}
// 进行合并,将关系在关系图谱中进行合并
void myunion(int i, int j,vector<int> &arr)
{
int i_fa = find(i, arr); // 寻找i的父节点
int j_fa = find(j, arr);// 寻找j的父节点
// 将i的父节点指向j,当然这里也可以将j的父节点指向i
arr[i_fa] = j_fa;
}
int main()
{
int n, m; // n表示有n个人,m表示有m组关系
cin >> n >> m;
// 读入m组关系
vector<pair<int, int>> record;
for (int i = 0; i < m; i++)
{
int a, b; cin >> a >> b;
record.push_back(make_pair(a,b));
}
vector<int> arr(n+1,0); // 用来存储本节点和其父节点的关系,下表表示本节点,内容表示其父节点。
// 进行初始化,初始化本节点的父节点为本节点
for (int i = 0; i < arr.size(); i++) arr[i] = i;
// 进行合并
for (const auto &item : record)
{
myunion(item.first, item.second,arr);
}
// 进行测试样例的测试
int q; cin >> q;
for (int i = 0; i < q; i++)
{
int a, b; cin >> a >> b;
int a_fa = find(a,arr);
int b_fb = find(b, arr);
if (a_fa == b_fb)cout << "Yes" << endl;
else cout << "No" << endl;
}
return 0;
}
/* 测试用例
10 7
2 4
5 7
1 3
8 9
1 2
5 6
2 3
3
3 4
7 10
8 9
输出:
Yes
No
Yes
*/