常见算法的c++实现总结

14 篇文章 3 订阅
7 篇文章 1 订阅

一、七大排序算法

#include <iostream>
#include <vector>
using namespace std;

void swap(int &a, int &b) {
	int tmp;
	tmp = a;
	a = b;
	b = tmp;
}

//一、插入排序
//1.1 直接插入排序  时间复杂度O(n^2)  空间复杂度O(1)  稳定
//构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
void insert_sort(vector<int> &nums) {
	for (int i = 0; i < nums.size(); i++){
		for (int j = i; j > 0; j--){
			if (nums[j] < nums[j - 1])
				swap(nums[j], nums[j - 1]);
		}
	}
}

//1.2 希尔排序  时间复杂度O(n^1.3)  空间复杂度O(1)  不稳定
//按照一定步长分成子序列进行排序,然后逐步递减步长来完成最终排序。增量 d = 1时, 就是插入排序
void shell_sort(vector<int> &nums) {
	for (int gap = nums.size() >> 1; gap>0; gap >>= 1){
		for (int i = gap; i < nums.size(); i++)
		{
			int tmp = nums[i];
			int j = i - gap;
			for (; j >= 0 && nums[j] > tmp; j -= gap){
				nums[j + gap] = nums[j];
			}
			nums[j + gap] = tmp;
		}
	}
}

//二、选择排序
//2.1 选择排序  时间复杂度O(n^2)  空间复杂度O(1)  不稳定
//先进行第一轮循环比较相邻两项,找到最小值得下标,将最小值与第一项交换,然后重复排序剩余部分。
void select_sort(vector<int> &nums) {
	for (int i = 0; i < nums.size(); i++) {
		int min = i;
		for (int j = i + 1; j < nums.size(); j++){
			if (nums[j] < nums[min])
				min = j;
		}
		swap(nums[i], nums[min]);
	}
}

//2.2 堆排序  时间复杂度O(nlgn)  空间复杂度O(1)  稳定
//构造一个最大堆(完全二叉树),父结点大于左右子结点,然后取出根结点(最大值)与最后一个结点交换,重复调整剩余的结点成最大堆,得到有序的序列。
void max_heapify(vector<int> &nums, int l, int r) {
	int curr = l;
	int child = curr * 2 + 1;
	while (child < r) {
		if (child + 1 < r &&nums[child] < nums[child + 1])
			child++;
		if (nums[curr] < nums[child]) {
			swap(nums[curr], nums[child]);
			curr = child;
			child = 2 * curr + 1;
		}
		else
			break;
	}
}

void heap_sort(vector<int> &nums) {
	for (int i = nums.size() / 2 - 1; i >= 0; i--)
	{
		max_heapify(nums, i, nums.size());
	}
	for (int i = nums.size() - 1; i > 0; i--)
	{
		swap(nums[0], nums[i]);
		max_heapify(nums, 0, i);
	}
}


//三、交换排序
//3.1 冒泡排序  时间复杂度O(n^2)  空间复杂度O(1)  稳定
//每次通过两两比较交换位置,选出剩余无序序列里最大(小)的数据元素放到队尾。
void bubble_sort(vector<int> &nums) {
	for (int i = 0; i < nums.size() - 1; i++){
		for (int j = 0; j < nums.size() - i - 1; j++){
			if (nums[j] > nums[j + 1])
				swap(nums[j], nums[j + 1]);
		}
	}
}

//3.2快速排序  时间复杂度O(nlgn)  空间复杂度O(1)  不稳定
//将无序序列分隔成两部分,左边均比右边小,分别对这两部分进行排序
void quick_sort(vector<int> &nums, int l, int r) {
	if (l < r) {
		int i = l, j = r;
		while (i < j) {
			while (nums[j] >= nums[l] && i < j)
				j--;
			while (nums[i] <= nums[l] && i < j)
				i++;
			swap(nums[i], nums[j]);
		}
		swap(nums[l], nums[i]);
		quick_sort(nums, l, i-1);
		quick_sort(nums, i + 1, r);
	}
}

//四、归并排序  时间复杂度O(nlgn)  空间复杂度O(n)  稳定
//将序列递归分成单位为1的小序列,然后两两合并排序,最终得到有序的序列。
void  merge_sort(vector<int> &nums, int l, int r, vector<int> &tmp) {
	if (l >= r) return;
	int m = (l + r) / 2;
	merge_sort(nums, l, m, tmp);
	merge_sort(nums, m + 1, r, tmp);
	int i = l, j = m + 1, index = l;
	while (i <= m &&  j <= r) {
		if (nums[i] < nums[j])
			tmp[index++] = nums[i++];
		else
			tmp[index++] = nums[j++];
	}
	while (i <= m)
		tmp[index++] = nums[i++];
	while (j <= r)
		tmp[index++] = nums[j++];
	for (int i = l; i <= r; i++)
		nums[i] = tmp[i];
}


int main() {

	cout << "原始数列:8, 3, 1, 4, 11, 2, 2, 1, 5, 2, 7, 9, 1, 6 " << endl;
	cout << "请选择排序算法:" << endl;
	cout << "1 插入排序,2 希尔排序,3 选择排序,4 堆排序,5 冒泡排序,6 快速排序,7 归并排序" << endl;

	int index;
	while (cin >> index){
		vector<int> nums{ 8, 3, 1, 4, 11, 2, 2, 1, 5, 2, 7, 9, 1, 6 };
		int len = nums.size();
		switch (index){
		case 1:
			insert_sort(nums); //插入排序
			break;
		case 2:
			shell_sort(nums); //希尔排序
			break;
		case 3:
			select_sort(nums); //选择排序
			break;
		case 4:
			heap_sort(nums); //堆排序
			break;
		case 5:
			bubble_sort(nums); //冒泡排序
			break;
		case 6:
			quick_sort(nums, 0, len - 1); //快速排序
			break;
		case 7:
			vector<int> tmp(len);
			merge_sort(nums, 0, len - 1, tmp); //归并排序
			break;
		}

		for (int i = 0; i<nums.size(); i++){
			cout << nums[i] << ' ';
		}
		cout << endl;
	}
	return 0;
}

在这里插入图片描述

二、大整数计算

#include <iostream>
#include <string>
using namespace std;

//大数相加
string addstr(string s1, string s2) {
	if (s1.size()>s2.size()) return addstr(s2, s1); //s1短
	s1.insert(s1.begin(), s2.size() - s1.size(), '0');
	string res(s2.size() + 1, '0');
	for (int i = s2.size() - 1; i >= 0; i--){
		int t = (s1[i] - '0') + (s2[i] - '0') + (res[i + 1] - '0');
		res[i + 1] = t % 10 + '0';
		res[i] = t / 10 + '0';
	}
	if (res[0] == '0') return res.substr(1);
	return res;
}
//大数相乘
string multiply(string num1, string num2) {
	int len1 = num1.size(), len2 = num2.size();
	string res(len1 + len2, '0');
	for (int i = len1 - 1; i >= 0; i--){
		for (int j = len2 - 1; j >= 0; j--){
			int t = (res[i + j + 1] - '0') + (num1[i] - '0')*(num2[j] - '0');
			res[i + j + 1] = t % 10 + '0';
			res[i + j] += t / 10;
		}
	}
	for (int i = 0; i<len1 + len2; i++){
		if (res[i] != '0') return res.substr(i);
	}
	return "0";
}

int main(){
	string s1, s2;
	cin >> s1 >> s2;

	cout << "相加:" << addstr(s1,s2) << endl;
	cout << "相乘:" << multiply(s1, s2) << endl;

	return 0;
}

在这里插入图片描述

三、编辑距离问题

题目地址

编辑距离,又称Levenshtein距离,是指两个子串之间,由一个转成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。请尝试写出一个算法来计算两个字符串的编辑距离。

1、当min(i,j)=0时,f(i,j)=max(i,j),一个字符串的长度为0,编辑距离是另一个字符串的长度
2、当a[i]=b[j]时,f(i,j)=f(i−1,j−1),比如xxcz和xyz的距离=xxc和xy的距离
3、否则,lev(i,j)为如下三项的最小值:
f(i−1,j)+1(在a中删除ai);
f(i,j−1)+1(在a中插入bj);
f(i−1,j−1)+1(在a中把ai替换bj),

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

//编辑距离
int EditDistance(string s1, string s2) {
	int m = s1.size(), n = s2.size();
	vector<vector<int>> arr(m + 1, vector<int>(n + 1));
	for (int i = 0; i <= m; i++){
		for (int j = 0; j <= n; j++){
			if (i == 0 || j == 0){
				arr[i][j] = max(i, j);
			}
			else if(s1[i-1] == s2[j-1]){ //注意
				arr[i][j] = arr[i - 1][j - 1];
			}
			else{
				arr[i][j] = min(arr[i - 1][j] + 1, min(arr[i][j-1] + 1, arr[i - 1][j - 1] + 1));
			}
		}
	}
	return arr[m][n];
}

int main(){
	string s1, s2;
	cin >> s1 >> s2;

	cout << EditDistance(s1, s2) << endl;

	return 0;
}

时间复杂度O(nm), 空间复杂度O(nm)

四、kmp算法

题目地址

给定两个字符串str和match,长度分别为N和M。实现一个算法,如果字符串str中含有子串match,则返回match在str中的开始位置,不含有则返回-1

#include <iostream>
#include <vector>
#include <string>
using namespace std;

// 暴力匹配算法
int ViolentMatch(string s, string p) {
	int i = 0, j = 0, slen = s.size(), plen = p.size();
	while (i < slen && j < plen)
	{
		if (s[i] == p[j]){
			i++;
			j++;
		}
		else{
			i = i - j + 1;
			j = 0;
		}
	}
	
	if (j == plen) return i - j;
	else return -1;
}

// KMP算法
int KMP(string s, string p, vector<int>next) {
	int i = 0, j = 0, slen = s.size(), plen = p.size();
	while (i < slen && j < plen)
	{
		if (j == -1 || s[i] == p[j]){
			i++;
			j++;
		}
		else{
			j = next[j];
		}
	}

	if (j == plen) return i - j;
	else return -1;
}

// MultiKMP算法
vector<int> MultiKMP(string s, string p, vector<int>next) {
	vector<int> res;
	int i = 0, j = 0, slen = s.size(), plen = p.size();
	while (i < slen)
	{
		if (j == -1 || s[i] == p[j]){
			i++;
			j++;
		}
		else{
			j = next[j];
		}
		if (j == plen){
			res.push_back(i - j);
			j = 0;
		}
	}
	return res;
}

// MultiKMP2算法
vector<int> MultiKMP2(string s, string p, vector<int>next) {
	vector<int> res;
	int i = 0, j = 0, slen = s.size(), plen = p.size();
	while (i < slen)
	{
		if (j == -1 || s[i] == p[j]){
			i++;
			j++;
		}
		else{
			j = next[j];
		}
		if (j == plen){
			res.push_back(i - j);
			i = i - j + 1;
			j = 0;
		}
	}
	return res;
}

vector<int> GetNext(string p)
{
	vector<int> next(p.size());
	next[0] = -1;
	int k = -1, j = 0;
	while (j < p.size() - 1)
	{
		//p[k]表示前缀,p[j]表示后缀  
		if (k == -1 || p[j] == p[k]){
			++k;
			++j;
			if (p[j] != p[k]) next[j] = k;
			else next[j] = next[k];
		}
		else{
			k = next[k];
		}
	}
	return next;
}

void printvec(vector<int> vec) {
	if (!vec.empty()){
		for (int i = 0; i < vec.size(); i++){
			cout << vec[i] << ' ';
		}
		cout << endl;
	}
	else{
		cout << -1 << endl;
	}
}

int main(){
	string s1, s2;
	cin >> s1 >> s2;

	vector<int> next = GetNext(s2);

	// 暴力匹配第一次出现位置
	cout << ViolentMatch(s1, s2) << endl;

	// KMP匹配第一次出现位置
	cout << KMP(s1, s2, next) << endl;

	// KMP匹配无重复所有出现位置
	vector<int> res = MultiKMP(s1, s2, next);
	printvec(res);

	// KMP匹配可重复所有出现位置
	vector<int> res2 = MultiKMP2(s1, s2, next);
	printvec(res2);

	return 0;
}

在这里插入图片描述
算法详解 时间复杂度为O(m + n)
扩展:BM算法、Sunday算法。

五、dijkstra算法

迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径。它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。它解决的是带权重的有向图上单源最短路径问题。
在这里插入图片描述

#include <iostream>
#include <string>

using namespace std;

const int maxn = 100; // 最大顶点数
int map[maxn][maxn]; // 邻接矩阵存图
int dis[maxn]; // dist[i]表示距离源点的最短距离
int vis[maxn];  // vis[i] = 1表示已加入集合S
int path[maxn]; // 记录路径
int n, m, r;

void dijk(int s) // 求顶点s到其他顶点的最短路径
{
	//初始化
	memset(path, -1, sizeof(path));
	/*INF使用0x3f3f3f3f的好处:
	* 1:满足无穷大加一个有穷的数依然是无穷大(在DijKstra算法松弛操作中避免了溢出而出现负数)
	* 2:满足无穷大加无穷大依然是无穷大(两个0x3f3f3f3f相加并未溢出)
	* 3:初始化时,由于每一个字节为0x3f,所以只需要memset(buf,0x3f,sizeof(buf))即可
	*/
	memset(dis, 0x3f, sizeof(dis)); //初始化为无穷大
	memset(vis, 0, sizeof(vis));
	dis[s] = 0; //自身到自身的距离为0
	while (1)
	{
		int k = 0;
		for (int j = 1; j <= n; j++)
		{
			if (!vis[j] && dis[j]<dis[k])//找未收录顶点中dis值最小的
				k = j; //这里第一次找到的是起点
		}
		if (!k) return; //没有未收录的点,则返回
		vis[k] = 1;
		//松弛操作
		for (int j = 1; j <= n; j++)
		{
			//第一次循环只有起点的邻接点距离被更新,每次都更新新找到的点的邻接点
			if (dis[j]>dis[k] + map[k][j])
			{
				dis[j] = dis[k] + map[k][j];
				path[j] = k;//路径被改变,重新记录前驱,最短路是由最短路+某一条固定路组成,所以前驱是有效的
			}
		}
	}
}

void printPath(int x)
{
	if (x == -1) 
		return;
	//递归
	printPath(path[x]);
	if (x == r) 
		cout << x;
	else 
		cout << " -> " << x;
}


/*问题描述:
* 输入n,m和r,代表n个节点,m条边,出发点r,然后是m行输入,每行有x,y,z,代表x到y的路距离为z。
* 问题:从r出发到各点的最短路径。
*/

int main()
{
	cin >> n >> m >> r;

	memset(map, 0x3f, sizeof(map));

	int x, y, z;
	for (int i = 1; i <= m; i++)
	{
		cin >> x >> y >> z;
		if (map[x][y] > z){ // 防止1->1的环
			map[x][y] = z;
			//map[y][x] = z; // 无向图
		}
	}

	dijk(r);

	for (int i = 1; i <= n; i++){
		printPath(i);
		cout<< " "<< dis[i];
		cout<< endl;
	}

	return 0;
}


在这里插入图片描述
算法详解 算法详解 时间复杂度为O( n^2)

六、0/1背包问题

在这里插入图片描述
0/1背包问题:满足总重量<=背包重量限制,使价值最大。

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int ZeroOnePack(int m, int s, vector<int> weight, vector<int> value) {
	// F[i ][ v] 表示  前i件物品恰放入一个容量恰为v的背包可以获得的最大价值
	vector<vector<int> > F(m + 1, vector<int>(s + 1));
	for (int i = 0; i <= s; i++) {
		F[0][i] = 0; //如果要求恰好装满背包,除了F[0]为0,其余F[1 .. s]均设为-INF
	}
	for (int i = 1; i <= m; i++){
		for (int j = 0; j <= s; j++){
			if (j >= weight[i - 1])
				F[i][j] = max(F[i - 1][j], F[i - 1][j - weight[i - 1]] + value[i - 1]);
			else
				F[i][j] = F[i - 1][j];
		}
	}

	//打印哪几样物品能够获得最大价值
	vector<bool> isAdd(m,false);
	int j = s;
	for (int i = m; i >= 1; i--){
		if (F[i][j] != F[i - 1][j]){
			isAdd[i - 1] = true;
			j = j - weight[i - 1];
		}
	}
	for (int i = 0; i < m; i++){
		cout << isAdd[i] << " ";
	}
	cout << endl;

	return F[m][s];
}

int main() {
	int m, s; // 物品种类m,背包总空间t,求最大价值
	cin >> m >> s;

	vector<int> weight;
	vector<int> value;
	for (int i = 0; i < m; i++){
		int t1, t2;
		cin >> t1 >> t2;
		weight.push_back(t1);
		value.push_back(t2);
	}

	int res = ZeroOnePack(m, s, weight, value);
	cout << res << endl;

	return 0;
}

在这里插入图片描述
算法详解

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值