排序——快速排序

news2024/10/7 2:27:39

目录

思想

演示

代码实现

解释

优化

三数取中

小区间优化

补充

挖坑法

双指针法

非递归实现


思想

     快速排序是一种二叉树结构的交换排序方法。

     基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

演示

     一般把序列的第一个元素认为基准值。定义两个变量一个是序列最右边一个是最左边(变量均为下标)。

09ad479c52504f9b9387557660440f67.png

     先让end走遇到比基准值小的停下来,然后让begin走遇到比基准值大的停下来,之后让它们交换位置。一直重复操作直至它们相遇。

d27f511fecf04b008ab32f3b232401f1.png

b93006cbb40f40c897e4163fe503105e.png

1e23283adf684531a2b28407657a8e81.png

d3633959a1c54bc19c5186675cd505fe.png1f609660eb054e3eaf83e280e10a886d.jpg

当它们相遇时,该位置的值一定小于基准值(在后面的解释中会有说明)。让该位置的值与基准值交换。

e928876ecbf241229a8a2d1d30d03031.png

     以上是一遍排序,还要继续排序下图的浅绿色序列和亮绿色序列,操作是和这次一样的只是范围不一样。在排完之后会继续再分用递归解决即可。

4c5f89cfdd964958aabbfc59a3e56539.png

     大概流程图如下 :

0d2a8dfee2bc4a5aac6f6aa0fe720cf9.png

     大家不能只看我画的图,也要自己画画图感受感受。

代码实现

//快速排序
void QuickSort(int* arr, int left, int right)
{
	if (left >= right)
		return;

	int keyi = left;
	int begin = left, end = right;
	while (begin < end)
	{
		while (begin < end && arr[end] >= arr[keyi])
			end--;
		while (begin < end && arr[begin] <= arr[keyi])
			begin++;

		Swap(&arr[begin], &arr[end]);
	}

	Swap(&arr[keyi], &arr[end]);

	keyi = begin;
	QuickSort(arr, left, keyi - 1);
	QuickSort(arr, keyi + 1, right);
}

      Swap函数是交换两个数。

void Swap(int* p1, int* p2)
{
	int temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}

解释

  • end遇到begin:
  1. begin没移动:那就是自己给自己交换一下位置。
  2. begin移动了:begin经历了交换它下面的值一定小于基准值。
  • begin遇到end:因为end先走,end停下的位置一定小于基准值。

     这也就解释了为什么要让end先走,如果要让begin先走那可能就不会当它们相遇时该位置的值一定小于基准值。

优化

三数取中

     快速排序的理想时间复杂度是O(NlogN),理想情况每次左右序列的长度相同时存在。如果该序列是有序或接近有序那时间复杂度就是O(N^2)如下图。这样快速排序的效率就会大打折扣。为了避免这种情况有人提出了三数取中。

c43a33fad8c54bd9b68e473bd572101a.png

     三数取中就是在序列中拿首元素,序列正中间的元素,尾元素这三个元素中挑一个中间元素来当基准值。这样做尽量保证每次的左右序列的长度相同,让它向理想情况靠近。

     当选出来时让该位置与序列首元素交换位置这么做是保证我们之前的逻辑。

//三位取中
int GetMidi(int* arr, int left, int right)
{
	int midi = (left + right) / 2;
	if (arr[left] < arr[midi])
	{
		if (arr[midi] < arr[right])
		{
			return midi;
		}
		else if (arr[left] > arr[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
	else
	{
		if (arr[midi] > arr[right])
		{
			return midi;
		}
		else if (arr[left] > arr[right])
		{
			return right;
		}
		else
		{
			return left;
		}

	}
}

小区间优化

     在上面提到过快速排序是一种二叉树结构的交换排序方法。

     它是一个二叉树结构,根据二叉树特点可知最后一层开辟的空间会约占全部的50%,它的上一层约占到25%。如果数据很多那么递归调用的深度就会很深有可能造成——栈溢出。

     最后的一两层里面包含的数据并不是很多,所以我们可以写一个判断,到特定的范围内用插入排序的方式来排序(这里我写的是当数据量小于10时),这样它的深度会变小很多。

补充

挖坑法

     这是针对排一遍方法的改进下面的双指针法也是一个道理。

     将第一个数据存在一个临时变量key中,从而形成一个坑位。让end先走当遇到比key小的数据时把end位置的值放入坑中,end位置就会形成一个坑。然后让begin走遇到比key大的就把它放入坑中。一直重复操作直至它们相遇。

4737ce536ba6427ab66de936fe6880fb.png

     最后它们相遇时把key放入它们相遇位置即可。

c8f74b82d5d24ce18febf24864dd5d15.png

双指针法

     先定义两个变量一个指向序列的首元素(prev),一个指向首元素的下一位(cur)。

     如果cur对应的值大于key(序列首元素),那就让cur独自向后走。如果小于key,让prev向后走然后让cur和prev交换位置如果cur等于prev那就不需要交换位置(下图是一部分移动过程)。

c8ba9d5565414bc8aa474127e2fb80a9.png

     当cur走出序列后,让prev与key进行交换。

     prev是在cur对应的值小于key的情况下才走固prev对应的值一定小于key。

非递归实现

     快速排序的非递归实现是基于栈实现的。在递归中只有范围在不断改变,所以我们只需要在栈里存放范围即可。

     要先在栈里存入序列的最左边的下标和最右边的下标。在排序时拿出下标。排完后再向栈中压入由它分出来的两个序列的左右下标。

//快速排序
void QuickSort(int* arr, int left, int right)
{
	ST st;
	STInit(&st);//初始化栈
	STPush(&st, right);//入栈
	STPush(&st, left);

	while (!STEmpty(&st))//判断栈是否为空
	{
		int begin = STTop(&st);//取出栈顶元素
		STPop(&st);//出栈
		int end = STTop(&st);
		STPop(&st);

		int keyi = begin;
		int prev = begin, cur = prev + 1;
		while (cur <= right)
		{
			if (arr[cur] < arr[keyi] && ++prev != cur)
			{
				Swap(&arr[prev], &arr[cur]);
			}
			cur++;
		}
		Swap(&arr[keyi], &arr[prev]);

		if (prev + 1 < end)//当只有一个数据时没必要入栈了
		{
			STPush(&st, end);
			STPush(&st, prev + 1);
		}
		if (begin < prev - 1)
		{
			STPush(&st, prev - 1);
			STPush(&st, begin);
		}
	}

	STDestroy(&st);//销毁栈
}

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

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

相关文章

从零开始理解 XML 和 JSON 的区别

在这篇文章中&#xff0c;我们将深入探讨XML和JSON这两种数据格式的关键异同点&#xff0c;以便读者可以根据项目需求做出明智的技术选择。 了解XML XML&#xff08;Extensible Markup Language&#xff09;是一种用于数据定义的标记语言&#xff0c;最初由万维网联盟&#x…

【调试笔记-20240618-Windows-pnpm 更新出现 Cannot find module 问题的解决方法】

调试笔记-系列文章目录 调试笔记-20240618-Windows-pnpm 更新出现 Cannot find module 问题的解决方法 文章目录 调试笔记-系列文章目录调试笔记-20240618-Windows-pnpm 更新出现 Cannot find module 问题的解决方法 前言一、调试环境操作系统&#xff1a;Windows 10 专业版调…

烂笔头笔记:为JDK安装Charles证书,让你的请求能够像在浏览器中那样被抓包

为什么要为JDK安装Charles证书 众所周知&#xff0c;https就是为了防止中间过程被拦截从而导致数据泄密的。若强行加入Charles代理&#xff0c;数据被解密后再被其重新加密&#xff0c;数据已经被“破坏”&#xff0c;客户端从而拒绝建立连接或解析内容。 #mermaid-svg-ksLo5W…

零样本学习及应用

传统的图像分类任务只是期望模型可以预测未见过图像的类别&#xff0c;而零样本学习则旨在预测从未见过的类别。对于原始图像分类任务来说&#xff0c;为每个类别收集大量训练数据对于最终拥有一个稳健的模型非常重要。然而&#xff0c;要获得大量的标注数据并不总是那么容易。…

【Unity设计模式】观察者模式,发布订阅模式,事件总线

前言 最近在学习Unity游戏设计模式&#xff0c;看到两本比较适合入门的书&#xff0c;一本是unity官方的 《Level up your programming with game programming patterns》 ,另一本是 《游戏编程模式》 这两本书介绍了大部分会使用到的设计模式&#xff0c;因此很值得学习 本…

Redis小对象压缩

小对象压缩存储 如果Redis内部管理的集合数据结构很小&#xff0c;他会使用紧凑存储形式压缩存储。 Redis的ziplist是一个紧凑的字节数组结构&#xff0c;如下图所示&#xff0c;每个元素之间都是紧挨着的。 如果他存储的是hash结构&#xff0c;那么key和value会作为两个ent…

UE5 C++ 跑酷游戏练习 Part1

一.修改第三人称模板的 Charactor 1.随鼠标将四处看的功能的输入注释掉。 void ARunGANCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) {// Set up action bindingsif (UEnhancedInputComponent* EnhancedInputComponent CastChecked&…

微服务开发与实战Day11 - 微服务面试篇

一、分布式事务 1. CAP定理 1998年&#xff0c;加州大学的计算机科学及Eric Brewer提出&#xff0c;分布式系统有三个指标&#xff1a; Consistency&#xff08;一致性&#xff09;Availability&#xff08;可用性&#xff09;Partition tolerance&#xff08;分区容错性&am…

“Git之道:掌握常用命令,轻松管理代码“

目录 1. 初始化和配置 2. 提交和更新 3. 分支和合并 4. 查看和比较 5. 远程仓库 6. 文件操作命令 1. 初始化和配置 git init&#xff1a;在当前目录初始化一个新的Git仓库git config&#xff1a;配置Git的全局或局部选项git clone&#xff1a;从远程仓库克隆一个本地副本…

MySQL基础——函数和约束

目录 1函数 1.1字符串函数 1.2数值函数 1.3日期函数 1.4流程函数 2约束 2.1约束概述和演示 2.2外键约束&#xff08;表连接键&#xff09; 1函数 函数是指一段可以直接被另一段程序调用的程序或代码。 1.1字符串函数 MySQL中内置了很多字符串函数&#xff0c;常用的…

【C语言习题】31.冒泡排序

文章目录 作业标题作业内容2.解题思路3.具体代码 作业标题 冒泡排序 作业内容 实现一个对整形数组的冒泡排序 2.解题思路 先了解一下冒泡排序&#xff1a; 两两相邻的元素进行比较&#xff0c;如果前面元素大于后面元素就交换两个元素的位置&#xff0c;最终的结果是最大的…

三星S20以上手机中的动态相片及其分解

三星S20以后的相机&#xff0c;相机拍出来的图片&#xff0c;用三星手机自带的“相册”打开之后&#xff0c;还会有“查看动态照片”的选项&#xff0c;点击之后就能查看拍照片时前后2秒左右的视频&#xff01; 不知道这个功能是不是三星独有的。 这样得到的图片非常大。因为…

一个简单的信号发射电路的构建

在基本的信号发射电路中&#xff0c;线圈&#xff08;电感器&#xff09;和电阻的组合可以产生振荡信号&#xff0c;而天线&#xff08;通常通过线圈&#xff09;用于发射信号。 LC振荡电路&#xff1a; **线圈&#xff08;L1&#xff09;和电容器&#xff08;C&#xff09;**串…

MySQL 基本语法讲解及示例(上)

第一节&#xff1a;MySQL的基本操作 1. 创建数据库 在 MySQL 中&#xff0c;创建数据库的步骤如下&#xff1a; 命令行操作 打开 MySQL 命令行客户端或连接到 MySQL 服务器。 输入以下命令创建一个数据库&#xff1a; CREATE DATABASE database_name;例如&#xff0c;创建一…

Locust框架

Locust 简介&#xff1a;是一个Python的第三方库&#xff0c;专门用来进行性能并发测试 Locust特点&#xff1a; 基于Python的开源负载测试工具支持多种操作系统支持二次开发能够模拟更多用户基于协程&#xff08;微线程&#xff09;的并发 Jmeter与Locust对比 Locust安装 …

街道网格宣传稿件投稿我知道了好方法

作为街道信息宣传员,我的日常是将街道的每一项重要活动、每一份温暖故事编织成文字,传递给公众。这份工作既充满挑战又极具意义,但在最初,我却在这份看似简单的任务上屡屡受阻。那时,我的投稿方式单一且传统——依赖电子邮件,将稿件发送至各大媒体的投稿箱。我本以为,只要内容足…

Linux操作系统学习:day03

内容来自&#xff1a;Linux介绍 视频推荐&#xff1a;[Linux基础入门教程-linux命令-vim-gcc/g -动态库/静态库 -makefile-gdb调试]( 目录 day0317、创建删除目录创建目录删除目录 18、文件的拷贝19、mv 命令20、查看文件内容的相关命令21、给文件创建软连接或硬链接 day03 …

docker部署dm数据库

官方文档参考 官网地址&#xff1a;https://eco.dameng.com/document/dm/zh-cn/start/dm-install-docker.html 下载镜像地址 docker部署 1、加载镜像 docker load -i dm8_20240613_x86_rh6_64_rq_ent_8.1.3.140_pack5.tar使用docker images&#xff0c;查看镜像和镜像标签…

【RK3588/算能/Nvidia智能盒子】挑战「无电无网」部署AI算法,守护大亚湾荃美石化码头工地安全

“万顷碧波之上&#xff0c;一座千米钢栈桥如蛟龙出水&#xff0c;向大海蜿蜒。钢栈桥上的项目建设者正在加紧作业&#xff0c;为助推惠州大亚湾加快建设成为世界级绿色石化基地全力奋战。”这是不久前北京日报对大亚湾惠州港荃湾港区荃美石化码头工地的描述。 △ 图片来源于北…

【RK3588/算能/Nvidia智能盒子】AI“值守”,规范新能源汽车充电站停车、烟火及充电乱象

近年来&#xff0c;中国新能源汽车高速发展&#xff0c;产量连续8年位居全球第一。根据中国充电联盟数据&#xff0c;截至2023年6月&#xff0c;新能源汽车保有量1620万辆&#xff0c;全国充电基础设施累计数量为665.2万台&#xff0c;车桩比约2.5:1。 虽然新能源汽车与充电桩供…