十大排序算法C++实现

分类

在这里插入图片描述

复杂度

排序稳定性定义
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,A1=A2,且A1在A2之前,而在排序后的序列中,A1仍在A2之前,则称这种排序算法是稳定的;否则称为不稳定的。
在这里插入图片描述

原理

讲的很清楚的up主

小灰的讲解让你完全拿捏10大排序算法

C++代码实现

#include <iostream>
#include <vector>
#include <map>
#include <string>

template<class T>
void swap(T &a, T &b) { T tmp = a;a = b;b = tmp; }
template<class T>
void print(std::vector<T> &v) { for(auto x : v) std::cout << x << " "; std::cout << std::endl; }

// 1.冒泡排序 
// 算法描述:比较相邻的元素,如果第一个比第二个大,就交换它们两个;
// 对每一对相邻元素作同样的工作,从第一对到最后一对,这样在最后的元素应该会是最大的数
// 针对所有的元素重复以上的步骤,除了最后一个;
// 重复步骤 1~3,直到排序完成 
void BubbleSort(std::vector<int> &v, int n) {
	bool flag;
	for(int i = 0; i < n - 1; ++i) {
		flag = true;
		for(int j = 1; j < n - i; ++j) {
			if(v[j] < v[j-1]) {
				swap(v[j], v[j-1]);
				flag = false;
			}
		}
		if(flag) break;
	}
	print(v);
}

// 2.插入排序 
// 算法描述:把区间分为左边已排序和右边未排序,初始已排序区间只有一个元素,就是数组第一个
// 每轮针对未排序区间的第一个元素,在已排序区间里找到合适的位置插入并保证数据一直有序
void InsertSort(std::vector<int> &v, int n) {
	for(int i = 1; i < n; ++i) {
		for(int j = i; j > 0 && v[j] < v[j-1]; --j) {
			swap(v[j], v[j-1]);
		}
	}
}

// 3.选择排序
// 算法描述:把区间分为左边已排序和右边未排序, 初始已排序区间没有元素 
// 每轮从未排序区间中找到最小值,然后置换到已排序区间的末尾 
void SelectSort(std::vector<int> &v, int n) {
	int Min_pos;
	for(int i = 0; i < n - 1; ++i) {
		Min_pos = i;
		for(int j = i; j < n; ++j) {
			if(v[Min_pos] > v[j]) Min_pos = j;
		}
		swap(v[i], v[Min_pos]);
	}
	print(v);
}

// 4. 堆排序 O(nlogn)
// 算法描述:利用堆这种数据结构所涉设计的一种排序算法。堆积是一个近似完全二叉树的结构
// 并同时满足堆积的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。
// 堆排序可以用到上一次排序的结果,所以不像其他一般的排序方法一样,每次都要进行 n-1 此的比较 
void Heapify(std::vector<int> &v, int n, int i) { // 维护堆的性质 
	while(1) { // 循环代替递归,调整节点i位置 
		int Max_pos = i; // 判断节点 i 是否满足大顶堆的性质 
		if(i * 2 + 1 < n && v[Max_pos] < v[i * 2 + 1]) Max_pos = i * 2 + 1;
		if(i * 2 + 2 < n && v[Max_pos] < v[i * 2 + 2]) Max_pos = i * 2 + 2;
		if(Max_pos == i) break; // 节点 i 满足性质 
		swap(v[i], v[Max_pos]); // 将Max值上浮,i节点下沉,大顶堆 
		i = Max_pos; // 继续调整 
	}
}
void HeapSort(std::vector<int> &v, int n) {
	// 1. 建堆 复杂度O(n) ,从完全二叉树的末端的父节点开始维护堆的性质
	for(int i = (n - 1) / 2; i >= 0; --i) { 
		Heapify(v, n, i);
	} 
	std::cout << v[0] << std::endl; 
	// 2. 进行堆排序, 将完全二叉树末端的元素与堆顶元素交换,然后删除完全二叉树末端的节点
	// 不断重复上述操作,直至堆中只有一个元素 
	// 排序过程中,就是从数组末端,不断放入当前堆顶的元素
	// 因此使用大顶堆排序出来就是升序 
	while(n > 1) {
		swap(v[0], v[n-1]);
		--n;
		Heapify(v, n, 0); // 对0号节点进行下沉调整,维护小顶堆性质 
	}
	print(v);
}

// 5. 希尔排序  也称递减增量排序  
// 算法描述:通过不断缩小增量进行插入排序,算法的最后一步就是普通的插入排序
// 就是取越来越小的步长进行插入排序,这样在最后步长为 1 的时候,只需要调整很少的几次就可以完成 
// 对插入排序的改进   可以让一个元素可以一次性地朝最终位置前进一大步
void ShellSort(std::vector <int> &v, int n) {
	for(int len = n / 2; len >= 1; len /= 2) {
		for(int i = len; i < n; ++i) {
			for(int j = i; j - len >= 0 && v[j - len] > v[j]; j -= len) {
				swap(v[j - len], v[j]);
			}
		}
	}
	print(v);
} 

// 6. 计数排序  
// 算法描述:先确定数据范围range,然后统计每个数字的个数,然后依次输出即可得到有序序列
// 适用范围:数据范围比较集中且为整数序列
void CountSort(std::vector<int> &v, int n) {
	// 1.计算元素范围 
	int Max = v[0], Min = v[0];
	for(auto x : v) {
		if(x < Min) Min = x;
		if(x > Max) Max = x;
	}
	// 2.统计元素个数 
	int range = Max - Min + 1;
	std::vector <int> c(range, 0); // 统计数组 
	for(auto x : v) c[x - Min]++; // 映射一下 
	// 如果不考虑排序稳定性, 在这就可以直接从小到大按个数输出了 
	
	// 保证其稳定性需要继续操作 
	// 3.求统计数组前缀和,此时的 c[x]-1 就等于元素x在排序完的序列中的最大位置 
	for(int i = 1; i < range; ++i) c[i] += c[i-1];
	// 4.倒序遍历原始数据,从统计数组中找到正确位置并输出
	std::vector <int> tmp_v(v);
	for(int i = n - 1; i >= 0; --i) {
		int x = tmp_v[i] - Min; // 得到映射值 
		v[c[x] - 1] = tmp_v[i]; // 在 c[x]-1 的位置上填上 tmp_v[i] 
		--c[x]; // 下个tmp_v[i]要在第c[x]-1个位置左边填上 
	}
	print(v);
} 


// 7. 桶排序
// 当数列取值范围过大,或者不是整数的时候,就无法用计数排序了 
// 类似于计数排序需要创建统计数组,桶排序需要创建若干个桶 
// 算法描述:分治的思想
template<class T>
void MergeSort(std::vector <T> &v, int l, int r); // 提前声明归并排序类模板 

void BucketSort(std::vector <double> &v, int n) {
	// 1.确定数列的范围 
	double Max = v[0], Min = v[0];
	for(auto x : v) {
		if(x < Min) Min = x;
		if(x > Max) Max = x;
	}
	// 2.初始化桶 
	int bknum = n;	// 一般创建的桶数量等于原始数列的元素数量
	std::vector<std::vector<double> > bk_list(bknum);
	// 除了最后一个桶只包含数列最大值,其余的桶均分最大值和最小值的差值. 按照比例确定
	// 桶的区间范围 = (最大值-最小值) / (桶的数量 - 1) 
	// 3.把元素都放入桶中
	double bk_range = (Max - Min) / (bknum - 1); 
	for(auto x : v) {
		// num = (x-Min) 除以区间跨度 
		int num = (int)((x - Min) / bk_range);
		bk_list[num].emplace_back(x);
	}
	// 4. 对桶内部排序   可以采用归并排序,也可以采用快速排序之类的
	for(int i = 0; i < bknum; ++i) {
		MergeSort(bk_list[i], 0, bk_list[i].size() - 1);
	}
	int j = 0;
	for(auto &x : bk_list) {
		for(auto &d : x) {
			v[j++] = d;
		}
	} 
	print(v);
}

// 8. 基数排序
// 针对手机号,英文单词等复杂元素的排序 
// 算法描述:把排序工作拆分成多个阶段,每一个阶段只根据一个字符进行计数排序,一共排序k轮 (k是元素长度)
// 基数排序相当于通过循环进行了多次桶排序
void RadixSort(std::vector <std::string> &v, int n) {
	int Max = 0;
	for(auto s : v) if(s.size() > Max) Max = s.size();
	std::vector <std::string> tmp_v(n);
	
	// 如果要对序列进行排序,k 应该从个位开始枚举
	for(int k = Max - 1; k >= 0; --k) {
		// 进行计数排序 
		std::vector <int> c(128, 0); // 统计所有字符串的第k位
		for(int i = 0; i < n; ++i) {
			int x = '0'; // 先假设需要补 0 
			if(k < v[i].size()) x = v[i][k]; // 判断一下字符是否有 k 位 
			c[x]++;
		}
		for(int i = 1; i < c.size(); ++i) c[i] += c[i-1];
		// 倒序找到正确位置并输出,保证稳定性
		for(int i = n - 1; i >= 0; --i) {
			int x = '0';
			if(k < v[i].size()) x = v[i][k];
			tmp_v[c[x] - 1] = v[i];
			c[x]--;
		}
		v.swap(tmp_v);
	}
	print(v);
}


// 9.快排 O(nlogn),最坏 O(n^2) 
// 算法描述: 选定Pivot中心轴,在原来的元素里根据这个枢纽分
// 将小于Pviot的元素放到Pivot的左边, 将大于Pviot的元素放到Pivot的右边
// 递归处理左右子序列 
void QuickSort(std::vector<int> &v, int l, int r) {
	if(l >= r) return;
	int first = l, last = r, pivot = v[l];
	while(first < last) {	 
		while(first < last && v[last] >= pivot) --last;//将小于piv的放到左边
		v[first] = v[last];
		while(first < last && v[first] <= pivot) ++first;//将大于piv的放到右边
		v[last] = v[first];
	}
	v[first] = pivot; // 更新pivot
	QuickSort(v, l, first);
	QuickSort(v, first + 1, r);
}

// 10.归并排序   O(nlogn)  稳定的排序算法,它不是原地排序算法
// 算法描述:将两个游标 i 和 j,分别指向 v[l, mid] 和 v[mid+1, r] 的第一个元素。比较这两个元素
// 如果 v[i]<v[j],就把 v[i] 放到临时数组 tmp, 并且 i 后移一位,否则将 v[j] 放入 tmp,j后移一位 
template<class T>
void MergeSort(std::vector <T> &v, int l, int r) {
	if(l >= r) return;
	int mid = (l + r) >> 1;
	MergeSort(v, l, mid);	  // 对 [l, mid] 区间排序
	MergeSort(v, mid + 1, r);//  对 [mid+1, r] 区间排序
	// 双指针对把俩个数组合并成一个有序数组 
	T *tmp = new T[r-l+1];	  // 暂时存放合并出的数组 
	int i = l, j = mid + 1, c = 0;
	while(i <= mid && j <= r) {
		if(v[i] < v[j]) tmp[c++] = v[i++];
		else tmp[c++] = v[j++];
	}
	while(i <= mid) tmp[c++] = v[i++];
	while(j <= r) tmp[c++] = v[j++];
	for(int k = 0; k < c; ++k) v[l+k] = tmp[k];
	delete[] tmp;
}


int main() 
{
	std::vector <int> v = {19, 97, 9, 17, 1, 8};
//	BubbleSort(v, v.size());
//	SelectSort(v, v.size());
//	SelectSort(v, v.size());
//	HeapSort(v, v.size());
//	ShellSort(v, v.size());
	CountSort(v, v.size());

	std::vector <double> d = {3.14, 2.66, 15.33, 20.0, 0.33};
	BucketSort(d, d.size());


	std::vector <std::string> v_s = {"121", "321", "12", "544", "312", "4354"};
	// 如果要对序列排序,不要用字符串的,用数字更方便,同时也要对函数进行修改 
	// 这种对字符串排序方式, 不足Max位的字符串,在末尾补 '0' 
	std::vector <std::string> v_ss = {"banana","apple","orange","ape","he"};
	// 正确排序结果:ape apple banana he orange 
	RadixSort(v_ss, v_ss.size());

	// 这俩函数是递归实现的,无法调用print,就放在最后了 
//	QuickSort(v, 0, v.size() - 1);
//	print(v);
//	MergeSort(v, 0, v.size() - 1);
//	print(v);
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/118689.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【电路笔记】-串联RLC电路分析

串联RLC电路分析 文章目录 串联RLC电路分析1、概述2、瞬态响应3、AC响应4、RCL和CLR配置5、结论 电阻器 、电感器 (L) 和电容器 © 是电子器件中的三个基本无源元件。 它们的属性和行为已在交流电阻、交流电感和交流电容文章中详细介绍。 在本文中&#xff0c;我们将重点讨…

2010年408计网

下列选项中, 不属于网络体系结构所描述的内容是&#xff08;C&#xff09;A. 网络的层次B. 每层使用的协议C. 协议的内部实现细节D. 每层必须完成的功能 本题考查网络体系结构的相关概念 再来看当今世界最大的互联网&#xff0c;也就是因特网。它所采用的TCP/IP 4层网络体系结…

Django初窥门径-项目初始化

环境准备 切换pypi源 运行下面的脚本将pypi源切换为阿里云镜像&#xff0c;避免安装python库的过程中出现网络问题 #!/bin/bash# 定义配置内容 config_content"[global] index-url http://mirrors.aliyun.com/pypi/simple/[install] trusted-hostmirrors.aliyun.com &…

机组 硬件

典型的冯诺伊曼计算机是以运算器为中心 现代的计算机已转化为以存储器为中心 运算器&#xff1a;完成算术运算和逻辑运算&#xff0c;并将运算的中间结果暂存在运算器内存储器&#xff1a;存放数据和程序控制器&#xff1a;控制、指挥程序和数据的输入、运行以及处理运算结果输…

react-app-env.d.ts是什么?

react-app-env.d.ts这个文件是使用CRA脚手架生成react项目时自动生成的&#xff0c;在平时的开发过程中看到这个文件就会感觉很疑惑&#xff0c;出于好奇心&#xff0c;在网上查找资料&#xff0c;得出下文 前置知识 这个是一个类型声明文件 它的内容很短&#xff0c;就一行…

成集云 | 电商平台、ERP、WMS集成 | 解决方案

电商平台ERPWMS 方案介绍 电商平台即是一个为企业或个人提供网上交易洽谈的平台。企业电子商务平台是建立在Internet网上进行商务活动的虚拟网络空间和保障商务顺利运营的管理环境&#xff1b;是协调、整合信息流、货物流、资金流有序、关联、高效流动的重要场所。企业、商家…

线性表(顺序表,单链表,双链表,循环链表,静态链表)

目录 1.线性表的定义1.几个重要的概念2.逻辑结构 2.线性表的基本操作3.顺序表&#xff08;线性表的顺序存储&#xff09;1.静态分配2.动态分配3.顺序表的特点4.顺序表的基本操作1.插入2.删除3.查找1.按位查找2.按值查找 4.链表&#xff08;线性表的链式存储&#xff09;1.单链表…

项目文章 | 总石油烃-重金属污染与土壤微生态系统:细菌多样性、组装和生态功能研究

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组许科研服务的易基因。 2023年9月30日&#xff0c;中南大学张杜博士为第一作者、李骞教授为通讯作者在《Chemosphere》杂志上发表题为“Effects of single and combined contamination of total petroleum hydr…

Single Image Haze Removal Using Dark Channel Prior(暗通道先验)

去雾算法都会依赖于很强的先验以及假设&#xff0c;并结合相应的物理模型&#xff0c;完成去雾过程。本文作者何凯明及其团队通过大量的无雾图像和有雾图像&#xff0c;归纳总结出无雾图像在其对应的暗通道图像上具有极低的强度值&#xff08;趋近于0&#xff09;&#xff0c;并…

HDD最后的冲刺:大容量硬盘的奋力一搏

1.引言 在上一篇文章&#xff08;微软Azure云数据中心工作负载分享&#xff1a;SSD与HDD&#xff0c;何去何从&#xff1f;&#xff09;中&#xff0c;我们提到在应对SSD QLC/PLC大容量的挑战中&#xff0c;HDD也是在不断的努力&#xff0c;推出HAMR&#xff0c;SMR等新介质。…

【验证码系列】Google验证码从数据训练到机器自动识别算法构建

文章目录 1. 写在前面2. CSCI级设计决策2.1. Google验证码突防关联2.2. Google验证码突防行为设计决策 3. Google验证码突防体系结构设计3.1. Google验证码突防部件3.1.2. Google验证码突防组成 3.2. Google验证码突防软件3.2.1. Google验证码突防软件体系结构3.2.2. Google验证…

信道编码译码及MATLAB仿真

文章目录 前言一、什么是信道编码&#xff1f;二、信道编码的基本逻辑—冗余数据1、奇偶检验码2、重复码 三、编码率四、4G 和 5G 的信道编码1、卷积码2、维特比译码&#xff08;Viterbi&#xff09;—— 概率译码3、LTE 的咬尾卷积码4、LTE 的 turbo 码 五、MATLAB 仿真1、plo…

Amazon Fargate使用Seekable OCI 实现更快的容器启动速度

前言 虽然在部署和扩展应用程序时&#xff0c;使用容器进行开发的方式已日趋流行&#xff0c;但仍有一些领域可以改进。扩展容器化应用程序的主要问题之一是启动时间长&#xff0c;尤其是在纵向扩展期间&#xff0c;需要添加较新的实例。此问题可能会对客户体验&#xff08;例如…

EfficientDet论文讲解

目录 EfficientDet 0、摘要 1、整体架构 1.1 BackBone&#xff1a;EfficientNet-B0 1.2 Neck&#xff1a;BiFPN特征加强提取网络 1.3 Head检测头 1.4 compound scaling 2、anchors先验框 3、loss组成 4、论文理解 5、参考资料 EfficientDet 影响网络的性能(或者说规…

Android Gldie复用只取之前decode过的缓存resource,Kotlin

Android Gldie复用只取之前decode过的缓存resource&#xff0c;Kotlin import android.graphics.Bitmap import android.os.Bundle import android.util.Log import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.life…

【Linux】服务器与磁盘补充知识,硬raid操作指南

服务器硬件 cpu 主板 内存 硬盘 网卡 电源 raid卡 风扇 远程管理卡 1.硬盘尺寸: 目前生产环境中主流的两种类型硬盘 3.5寸 和2.5寸硬盘 2.5寸硬盘可以通过使用硬盘托架后适用于3.5寸硬盘的服务器 但是3.5寸没法转换成2.5寸 2.如何在服务器上制作raid 华为服务器为例子做…

基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(一)

熟悉项目环境 1. 苍穹外卖项目介绍1.1 项目介绍1.2 技术选型 2. 开发环境搭建2.1 前端环境2.2 后端环境搭建2.3 Git版本控制2.4 nginx反向代理和负载均衡 3.登录功能4. Swagger4.1 介绍4.2 使用步骤4.3 常用注解 1. 苍穹外卖项目介绍 1.1 项目介绍 苍穹外卖是专门为餐饮企业&…

JAVA前端开发介绍

以一个网站为例包括网站设计、前端开发、程序开发等。网站设计就是网站的外观&#xff0c;平面的东西。程序开发也好理解就是功能实现。而前端开发&#xff0c;简单来说&#xff0c;就是把平面效果图转换成网页&#xff0c;把静态转换成动态。它的工作包括了:切图、写样式、做鼠…

rust闭包

rust闭包 参考 Rust有三个闭包trait&#xff1a;Fn、FnMut和FnOnce&#xff0c;编译器会根据闭包内代码的行为自动为闭包实现这些trait。 上面这段话超级重要&#xff01;&#xff01;&#xff01; 对于不可变或移动捕获变量的闭包&#xff0c;编译器会实现Fn trait&#xff0…

三维模型几何坐标精度偏差应采用主要措施

三维模型几何坐标精度偏差应采用主要措施 降低倾斜摄影三维模型几何精度偏差是提高模型质量和准确性的关键任务。下面将浅谈降低倾斜摄影三维模型几何精度偏差应采用的主要措施。 1、倾斜角度选择&#xff1a;倾斜角度对于几何精度具有重要影响。选择适当的倾斜角度可以优化视…