【数据结构】常见的排序算法

news2024/11/17 9:33:24

常见的排序算法

  • 常见的排序算法
  • 插入排序之直接插入排序
    • 时间复杂度
    • 特性总结
  • 插入排序之希尔排序
    • 时间复杂度
  • 选择排序之直接选择排序
    • 特性总结
  • 选择排序之堆排序
    • 时间复杂度
    • 特性总结
  • 交换排序之冒泡排序
    • 特性总结
  • 交换排序之快速排序
    • hoare版本
    • 挖坑法
    • 双指针法
    • 快速排序的优化1,增加三数取中
    • 快速排序的优化2,将递归算法改为非递归算法
    • 快速排序的性能总结
  • 归并排序
    • 归并排序特性总结

常见的排序算法

常见的七大排序算法:
在这里插入图片描述

插入排序之直接插入排序

在这里插入图片描述

void InsertOrder(vector<int>& v)
{
	for (int i = 0; i < v.size() - 1; ++i)
	{
		//起始end为0
		int end = i ;
		//比较的值是下标为end的下一个
		int tmp = v[end + 1];
		while (end >= 0)
		{
			//如果end对应的值比tmp大,说明需要进行插入排序
			if (v[end] > tmp)
			{
				v[end + 1] = v[end];
				//插入完成后end要向前走
				end--;
			}
			//如果end对应的值比tmp小,说明不需要进行插入,跳出当前while循环,end向后走即可
			else
				break;
		}
		//一趟完成后,要将tmp赋给end的下一个位置
		v[end + 1] = tmp;
	}
}

时间复杂度

最好的情况是O(n),数组为升序
最坏的情况是O(n2),数组为降序

特性总结

  1. 元素集合越接近有序,直接插入排序算法的时间效率越高
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1),它是一种稳定的排序算法
  4. 稳定性:稳定

插入排序之希尔排序

在这里插入图片描述

针对直接插入排序中,当数组属于降序时,时间复杂度太高,设计了希尔排序
设计思路是:当数组降序时,使用分组,可以减小排序次数,逐渐减小分组间隙,当排序次数较多时,数组本身已经快要接近有序了,以此来解决数据降序时排序复杂度高的问题。
因此希尔排序是对直接插入排序的优化

void ShellSort(vector<int>& v)
{
	int gap = v.size();
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < v.size() - gap; ++i)
		{
			int end = i ;
			int tmp = v[end + gap];
			while (end >= 0)
			{
				if (v[end] > tmp)
				{
					v[end + gap] = v[end];
					end -= gap;
				}
				else
					break;
			}
			v[end + gap] = tmp;
		}
	}
}

时间复杂度

O(n1.25)

选择排序之直接选择排序

设计思路是:
1.从前往后遍历整个数组,拿到当前数组最大和最小值
2.将最小值和第一个元素交换,最大值和最后一个元素交换
3.缩小数组的范围,继续上述的操作
4.直到新数组范围内只有一个元素为止
在这里插入图片描述

void swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
void SelectSort(vector<int>& v)
{
	int begin = 0;
	int end = v.size() - 1;
	while (begin < end)
	{
		int mini = begin;
		int maxi = begin;
		for (int i = begin; i <= end; ++i)
		{
			if (v[mini] > v[i])
				mini = i;
			if (v[maxi] < v[i])
				maxi = i;
		}
		if (maxi == begin)
			maxi = mini;
		swap(&v[mini], &v[begin]);
		swap(&v[maxi], &v[end]);
		begin++;
		end--;
	}
}

特性总结

  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定

选择排序之堆排序

思路:通过使用大堆或者小堆的思路,将数组进行排序
1.排升序建大堆,排降序建小堆
2.升序为例,先建一个大堆(双亲节点都大于左右孩子,根节点的值最大)
3.将数组最后一个位置的值和第一个位置的值交换
4.堆中除了最后一个节点,其余的节点进行调整,调整为新的大堆,将新的大堆的根节点值和倒数第二个节点的值交换
5.以此类推,直到当前新堆的范围为1停止
6.堆排序完成

void adjustDown(int* a, int n, int parent) {
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child+1 < n && a[child] < a[child + 1]) {
			child++;
		}
		if (a[child] > a[parent]) {
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else {
			break;
		}
	}
}
void HeapSort(int* a, int n) {
	//1、建堆
	//parent = (child-1)/2
	for (int i = (n-1-1)/2; i >= 0; i--)
	{
		adjustDown(a, n, i);
	}
	//2、堆排序
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		end--;
		adjustDown(a, end, 0);
	}
}

时间复杂度

O(nlogn)

特性总结

  1. 堆排序使用堆来选数,效率就高了很多。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定

交换排序之冒泡排序

思想:就是从前往后,依次判断当前位置的值和后一个位置的值关系,如果当前值大于后一个位置的值,就进行交换,这样将最大的值就放到的最后面,依次让数组的范围从后往前减小,循环遍历数组,就可完成排序

void BobbleSort(int* v, int size)
{
	int exchage = 0;
	for (int i = 0; i < size ; ++i)
	{
		for (int j = 0; j < size - i - 1; ++j)
		{
			if (v[j] > v[j + 1])
			{
				swap(v[j], v[j + 1]);
				exchage = 1;
			}
		}
		if (exchage == 0)
			break;
	}
}

特性总结

  1. 冒泡排序是一种非常容易理解的排序
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:稳定

交换排序之快速排序

hoare版本

在这里插入图片描述

思想:将某个元素作为基准值,数组被该元素分为两个子数组,左子数组中的值均小于基准值,右子数组中的值均大于基准值,左右子数组重复上述过程,最终实现排序

做法
1.先设置数组的第一个元素为基准值,设置第一个位置为左,最后一个位置为右
2.右先走,找比基准值小的,左后走,找比基准值大的
3.交换左右两个位置的值
4.右继续向左走,左继续向右走
以上的条件都是在左小于右的基础上进行的
5.如果相遇则将基准值和相遇值交换,函数返回相遇点

int _QuickSort(int* v, int left, int right)
{
	int k = left;
	while (left < right)
	{
		//从右往左遍历,找比k小的数
		while (left < right && v[right] >= v[k])
		{
			right--;
		}
		//从左往右遍历,找比k大的数
		while (left < right && v[left] <= v[k])
		{
			left++;
		}
		//找到之后,进行交换
		swap(&v[left], &v[right]);
	}
	//如果相遇,将相遇点和数组左边的值交换
	swap(&v[left], &v[k]);
	return left;
}
void QuickSort(int* v, int left, int right)
{
	if (left >= right)
		return;
	int meet = _QuickSort(v, left, right);
	QuickSort(v, left, meet - 1);
	QuickSort(v, meet + 1, right);
}

挖坑法

在这里插入图片描述

思路
将某个元素作为基准值,数组被该元素分为两个子数组,左子数组中的值均小于基准值,右子数组中的值均大于基准值,左右子数组重复上述过程,最终实现排序
做法
1.设置一个坑,hole为最左边的下标,一个key值
2.让右下标向左走,找比key对应值小的,让坑对应的值赋给右下标位置,并且让右下标表示为hole
3.让左下标向右走,找比key对应值大的,让坑对应的值赋给左下标位置,并且让左下标表示为hole
如果遇到左下标大于等于右下标的情况,则将key对应的值赋给相遇点
上述操作是在左小于右的范围下进行的
4.递归进行上述操作

int _QuickSort1(int* v, int left, int right)
{
	int hole = left;
	int key = v[left];
	while (left < right)
	{
		while (left < right && v[right] >= key)
			right--;
		v[hole] = v[right];
		hole = right;

		while (left < right && v[left] <= key)
			left++;
		v[hole] = v[left];
		hole = left;
	}
	v[left] = key;
	return left;
}
void QuickSort1(int* v, int left, int right)
{
	if (left >= right)
		return;
	int meet = _QuickSort1(v, left, right);
	QuickSort1(v, left, meet - 1);
	QuickSort1(v, meet + 1, right);
}

双指针法

在这里插入图片描述

思想:和hoare版本和hole版本相似,都是将当前数组分为两个子数组,递归进行排序
做法:
1.设置key等于数组的左下标,left和right分别是数组的最左和最右,设置prev为左,prev的下一个为cur
2.先让cur向右走,找比key对应的值小的数
3.让prev++,交换prev和cur对应的两个数
4.继续上述操作
5.当cur超出right范围时,停止循环,让key和prev对应的值交换

int _QuickSort2(int* v, int left, int right)
{
	int prev = left;
	int cur = left + 1;
	int key = left;
	while (cur <= right)
	{
		if (v[cur] < v[key])
		{
			prev++;
			swap(&v[prev], &v[cur]);
		}
		cur++;
	}
	swap(&v[key], &v[prev]);
	return prev;
}
void QuickSort(int* v, int left, int right)
{
	if (left >= right)
		return;
	int meet = _QuickSort2(v, left, right);
	QuickSort(v, left, meet - 1);
	QuickSort(v, meet + 1, right);
}

快速排序的优化1,增加三数取中

在这里插入图片描述
快速排序由于其思想是:根据基准值将数组划分为两个子区间,通过不断的划分子区间将其进行排序,但是当被排序数组是有序的,那么就会退化成下面图示的情况,导致复杂度为O(n2)。
在这里插入图片描述
使用三数取中的方式,将数组可以平均二分,从而提高效率

//三数取中算法
int SelectMidIndex(int* v, int left, int right)
{
	int mid = left + (right - left) >> 1;
	if (v[left] > v[mid])
	{
		if (v[right] < v[mid])
			return mid;
		else if (v[right] > v[left])
			return left;
		else return right;
	}
	else //v[left] < v[mid]
	{
		if (v[right] > v[mid])
			return mid;
		else if (v[right] < v[left])
			return left;
		else return right;
	}
}
//hoare版本
int _QuickSort(int* v, int left, int right)
{
	int mid = SelectMid(v, left, right);
	swap(v[mid], v[left]);
	int k = left;
	while (left < right)
	{
		//从右往左遍历,找比k小的数
		while (left < right && v[right] >= v[k])
		{
			right--;
		}
		//从左往右遍历,找比k大的数
		while (left < right && v[left] <= v[k])
		{
			left++;
		}
		//找到之后,进行交换
		swap(&v[left], &v[right]);
	}
	//如果相遇,将相遇点和数组左边的值交换
	swap(&v[left], &v[k]);
	return left;
}


//hole版本
int _QuickSort1(int* v, int left, int right)
{
	int mid = SelectMid(v, left, right);
	swap(v[mid], v[left]);
	int hole = left;
	int key = v[left];
	while (left < right)
	{
		while (left < right && v[right] >= key)
			right--;
		v[hole] = v[right];
		hole = right;

		while (left < right && v[left] <= key)
			left++;
		v[hole] = v[left];
		hole = left;
	}
	v[left] = key;
	return left;
}


//双指针法
int _QuickSort2(int* v, int left, int right)
{
	int mid = SelectMidIndex(v, left, right);
	swap(&v[mid], &v[left]);
	int prev = left;
	int cur = left + 1;
	int key = left;
	while (cur <= right)
	{
		if (v[cur] < v[key] && ++prev != cur)
			swap(&v[prev], &v[cur]);
		cur++;
	}
	swap(&v[key], &v[prev]);
	return prev;
}

快速排序的优化2,将递归算法改为非递归算法

我们可以将递归的快速排序算法改为非递归的方法,或者采用栈的数据结构作为辅助

快速排序的性能总结

  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(logN)
  4. 稳定性:不稳定

归并排序

在这里插入图片描述

思路:采用分治法的思想,将已有的子序列合并,得到完全有序的序列,让每个子序列有序,再让两个有序列表合并为一个有序列表,称为二路归并

void _MargeSort(int* v, int left, int right, int* tmp)
{
	if (left >= right)
		return;
	int mid = left + (right - left)/2;
	_MargeSort(v, left, mid, tmp);
	_MargeSort(v, mid + 1, right, tmp);
	int begin1 = left;
	int end1 = mid;
	int begin2 = mid + 1;
	int end2 = right;
	int index = begin1;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (v[begin1] < v[begin2])
			tmp[index++] = v[begin1++];
		else 
			tmp[index++] = v[begin2++];
	}
	while(begin1 <= end1)
		tmp[index++] = v[begin1++];
	while (begin2 <= end2)
		tmp[index++] = v[begin2++];
	for (int i = left; i <= right; ++i)
		v[i] = tmp[i];
}
void MargeSort(int* v, int n)
{
	int left = 0;
	int right = n-1;
	int* tmp = (int*)malloc(n * sizeof(int*));
	_MargeSort(v, left, right, tmp);
	free(tmp);
}

归并排序特性总结

  1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(N)
  4. 稳定性:稳定

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(YouTube)KDBA QML 学习笔记1

&#xff08;YouTube&#xff09;KDBA QML 学习笔记 旧版本(QML文件介绍) main.qml import QtQuick 2.0Text {text: "Hell World" }main.cpp #include <QtQuick>int mian(int argc, char *argn[]) {QGuiApplication app(argc, argv);//QT开始 QQuickvi…

maven下载安装及初次使用相关配置

maven下载按照及初次使用相关配置 一、下载 与安装 下载完解压放在文件夹中即可&#xff01; 依赖Java&#xff0c;需要配置JAVA_HOME设置MAVEN自身的运行环境&#xff0c;需要配置MAVEN_HOME&#xff08;参考安装java&#xff09;测试环境配置结果 MVN测试成功&#xff01…

大牛练成记:用JavaScript徒手写出一个日期选择插件

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;本文已收录于专栏&#xff1a;100个JavaScript的小应用。 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收…

OpenMMLab MMDetectionV3.1.0-SAM(环境安装、模型测试、训练以及模型后处理工具)

OpenMMLab Playground 概况 当前通用目标检测的研究方向正在朝着大型多模态模型发展。除了图像输入之外&#xff0c;最近的研究成果还结合了文本模式来提高性能。添加文本模态后&#xff0c;通用检测算法的一些非常好的属性开始出现&#xff0c;例如&#xff1a; 可以利用大量…

【多线程系列-04】深入理解java中线程间的通信机制

多线程系列整体栏目 内容链接地址【一】深入理解进程、线程和CPU之间的关系https://blog.csdn.net/zhenghuishengq/article/details/131714191【二】java创建线程的方式到底有几种&#xff1f;(详解)https://blog.csdn.net/zhenghuishengq/article/details/127968166【三】深入…

如何通过 5 步激活策略扩大用户群

假设&#xff0c;你现在是一个“深藏功与名”的增长黑客。前期你表现非常好&#xff0c;做了一些拉新实验&#xff0c;每天都有上千用户进入到产品。团队成员和家人朋友都非常开心你们的产品增长终于有了起色。 然而&#xff0c;如果你不重视拉新&#xff08;acquisition&…

TI的IWR6843跑3D People Tracking(3D人体检测追踪实验)demo的上手教程

1.硬件准备 1.IWR6843板子 2.两个USB转串口模块&#xff08;因为我的是自己做的板子&#xff0c;板子上没有集成USB转串口芯片&#xff09; 2.软件准备 1.最新版本的CCS&#xff0c;注意后缀没有THEIA https://www.ti.com/tool/CCSTUDIO?DCMPdsp_ccs_v4&HQSccs 2.最新…

速通pytorch库

速通pytorch库&#xff08;长文&#xff09; 前言 ​ 本篇文章主要为那些对于pytorch库不熟悉、还没有上手的朋友们准备&#xff0c;梳理pytorch库的主要内容&#xff0c;帮助大家入门深度学习最重要的库之一。 目录结构 文章目录 速通pytorch库&#xff08;长文&#xff09;1.…

13 springboot项目——准备数据和dao类

13.1 静态资源下载 https://download.csdn.net/download/no996yes885/88151513 13.2 静态资源位置 css样式文件放在static的css目录下&#xff1b;static的img下放图片&#xff1b;template目录下放其余的html文件。 13.3 创建两个实体类 导入依赖&#xff1a;lombok <!…

35岁后的测试工程师出路?测试工程师能干多久?

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 你有没有一刹那意…

服务器硬件、部署LNMP动态网站、部署wordpress、配置web与数据库服务分离、配置额外的web服务器

day01 day01项目实战目标单机安装基于LNMP结构的WordPress网站基本环境准备配置nginx配置数据库服务部署wordpressweb与数据库服务分离准备数据库服务器迁移数据库配置额外的web服务器 项目实战目标 主机名IP地址client01192.168.88.10/24web1192.168.88.11/24web2192.168.88…

产业大数据应用:精准剖析区域产业,摸家底 明方向 促发展

随着信息技术的飞速发展&#xff0c;大数据和新一代信息技术的崛起&#xff0c;这些技术的应用正在逐渐渗透到各个领域&#xff0c;在区域产业发展上&#xff0c;他们不仅为区域产业诊断分析带来了高效的工具&#xff0c;更为区域制定产业发展战略和政策提供了有效的数据支撑。…

活动预告 | 中国数据库联盟(ACDU)中国行第二站定档杭州,邀您探讨数据库技术与实践!

数据库技术一直是信息时代中不可或缺的核心组成部分&#xff0c;随着信息量的爆炸式增长和数据的多样化&#xff0c;其重要性愈发凸显。作为中国数据库联盟&#xff08;ACDU&#xff09;的品牌活动之一&#xff0c;【ACDU 中国行】在线下汇集数据库领域的行业知名人士&#xff…

Keil出现Flash Timeout.Reset the Target and try it again.我有一种解决方法

2.解决方法 网上查找了找原因&#xff0c;是因为之前代码设置了读保护功能。 读保护即大家通常说的“加密”&#xff0c;是作用于整个Flash存储区域。一旦设置了Flash的读保护&#xff0c;内置的Flash存储区只能通过程序的正常执行才能读出&#xff0c;而不能通过下述任何一种…

echarts绘制关系图

效果图&#xff1a; 代码&#xff1a; <template><div id"serveGraph" style"height: 100%; width: 100%; z-index: 88;"></div> </template> <script> import { defineComponent,reactive,toRefs,onMounted,watch } from …

网络工程师 快速入门

需要掌握 以下技术 1.网络 基础 知识 TCP/IP 、OSI 7层协议、IP地址、ARP地址解析协议、ICMP&#xff08;英特网控制报文协议&#xff0c;ping&#xff09;等 入门面试常问问题。 2.路由 路由匹配 三原则、静态路由、OSPF路由协议。 2.交换 如何放数据&#xff1f; VLAN TRU…

【Spring】bean的生命周期

1.具体的生命周期过程 bean对象创建&#xff08;调用无参构造器&#xff09; 给bean对象设置属性 bean对象初始化之前操作&#xff08;由bean的后置处理器负责&#xff09; bean对象初始化&#xff08;需在配置bean时指定初始化方法&#xff09; bean对象初始化之后操作&am…

C# Blazor 学习笔记(0.1):如何开始Blazor和vs基本设置

文章目录 前言资源推荐环境如何开始Blazor个人推荐设置注释快捷键热重载设置 前言 Blazor简单来说就是微软提供的.NET 前端框架。使用 WebAssembly的“云浏览器”&#xff0c;集成了Vue,React,Angular等知名前端框架的特点。 资源推荐 微软官方文档 Blazor入门基础视频合集 …

Arcgis地图实战一:单个图层中设施的隐藏及显示

文章目录 1.效果图预览2.弹框的实现3.显示及隐藏的实现 1.效果图预览 2.弹框的实现 let alert this.alertCtrl.create();alert.setTitle(请选择设施);for (let item of this.ctralllayers) {alert.addInput({type: checkbox,label: item.name,value: item.id,checked: item.vi…

音量压低处理流程

开始 通过申请临时DUCK焦点可以压低其他在播放的音源&#xff0c;如源码中的注释&#xff0c;不会暂停其他在播放的音源&#xff0c;而是降低输出&#xff0c;在车载情景下&#xff0c;一般在地图导航或者语音播报的情景下会申请这个焦点。 // AudioManager.java /*** Used t…