c++常用算法总结

该文章长期更新

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
*/
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
### 回答1: C/C++算法常用手册是程序员日常工作中不可或缺的工具书之一。该手册主要收录了程序员在开发过程中常用算法,以及相应的代码现。该手册涵盖了诸如数据结构、排序、查找、递归、贪心、动态规划、字符串等算法,帮助程序员快速掌握这些算法的基本原理和现方式。简单地说,该手册将算法的核心原理和现细节集中在了一起,兼顾了易懂性和用性。 随着程序员需求的不断增加,该手册逐渐扩充了更多的算法类型。同时,该手册还根据算法的不同应用场景进行分类,方便程序员快速查找和使用。例如,程序员可以通过该手册快速了解不同数据结构的原理和现方法,了解常见算法的时间复杂度和空间复杂度,还可以查找常见的际问题中的算法现方式。 总的来说,C/C++算法常用手册是程序员必备的工具之一,帮助程序员提高算法现能力和解决际问题的能力,提高程序的效率和质量。 ### 回答2: C/C++常用算法手册是一本介绍计算机算法的参考手册,主要面向C/C++语言程序员。该手册总结了各种常用算法包括排序、查找、图论、字符串等。通过该手册的学习,可以让程序员更好地掌握C/C++编程的技巧和方法。 该手册中介绍了排序算法包括冒泡排序、插入排序、选择排序、快速排序、归并排序等。对于不同的排序算法,手册详细介绍了它们的思路和现方法,同时也对它们的时间复杂度和效率进行了分析和比较。 在查找方面,手册介绍了常用的顺序查找和二分查找算法,它们可以帮助程序员快速地定位和查找数据。 在图论和字符串方面,手册介绍了很多有用的算法,如最短路径算法、最小生成树算法、字符串匹配算法等。这些算法可以帮助程序员更好地解决际问题。 总之,C/C++常用算法手册是一本非常用和有价值的参考书,它可以让程序员掌握更多的C/C++算法技巧,提高程序员的编程能力和开发效率。 ### 回答3: C/C++ 常用算法手册是一本总结了 C/C++ 编程语言中常用算法、数据结构、设计模式等知识的参考书籍。 相对于其他语言,C 和 C++ 语言有着更高的执行效率和更多的编程自由度,也因此被广泛应用于开发高性能、底层的软件程序。在这样的应用场景下,对算法和数据结构的掌握显得尤为重要。 C/C++ 常用算法手册涵盖了各种基础的算法和数据结构,比如排序、查找、链表、树等。同时,它也介绍了一些常用的高级算法,比如动态规划、贪心算法和回溯算法。 此外,该手册还详细说明了面向对象编程领域中常用的设计模式和其现方式,例如工厂模式、装饰器模式等。 阅读 C/C++ 常用算法手册不但能够让读者掌握常用算法现方法,更能提高编程思维和技巧。另外,在际应用中,编高效的程序不仅需要算法的巧妙运用,更需要细致、严谨的代码风格和设计思路。此时,该手册中丰富的示例代码和编码规范性的讲解也能为读者提供很大的帮助。 总之,C/C++ 常用算法手册是一本既用又深入的参考书,适合广大 C/C++ 开发者和算法学习者阅读。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

思禾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值