快速排序【hoare版本】【挖坑法】【双指针法】(数据结构)

       快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

一、hoare版本

       该算法的大体框架为:假设取数组的头为key同时保存索引变量begin的值在此处,取key的另一端为索引变量end,end不断向左移动,直至处于一个小于key的值的位置,此时再让索引变量begin不断向右移动,直至处于一个大于key的值的位置,交换两个值。由此不断循环往复,最终肯定会在一个大于key值的位置处end和begin相遇,最终交换该值与key所在的位置,即可得到一个左边比key小,右边比key大的一个数组。

       由于数组是以key为基准,左右两边分别小于和大于key,所以该数组又可分为[left,key-1] , [key+1,right],然后递归调用上面的算法,左右子序列重复该过程,直至所有元素都排列在相应位置上即可。

下图为单趟快速排序的过程

程序源代码

//单趟排序
int PartSort1(int* a, int begin, int end)
{
	int key = a[begin];//选取左边做key
	int keyindex = begin;
	while (begin < end)
	{
		while (begin < end && a[end] >= key)//此处必须要在此判断begin<end,防止end越界
		{
			end--;
		}
		while (begin < end && a[begin] <= key)
		{
			begin++;
		}
		Swap(&a[end], &a[begin]);
	}
	//此时begin == end
	Swap(&a[end], &a[keyindex]);//此处不能跟key交换,因为key是局部变量
	return begin;
}
void QuickSort(int* a, int left , int right)
{
	assert(a);
	if (left >= right)
	{
		return;
	}
	int div = PartSort1(a, left, right);
	//[left,div - 1]  div  [div + 1 , right]
	QuickSort(a, left, div - 1);
	QuickSort(a, div + 1, right);
}

注意事项

1.若选取右边的值做key,那么一定要让左边的begin先走,这样能保证他们性欲的位置一定是一个比key大的位置...(选取左边值做key同理)

2.注意在移动begin和end的时候每次都需要判断begin<end,防止begin和end相遇之后错过,无法进行正确的排序

优化

       经过分析我们发现:快速排序的最坏情况是每次选择的基准元素都是当前子数组的最大或最小值,导致每次划分只能减少一个元素的规模。在这种情况下,算法的时间复杂度接近于O(N*N),快速排序也就没有什么优势了,因此我们要做出优化。 

       为避免选取到最大值或者最小值,出现了三数取中法。即选取三个数的中间值作为基准,就可以很好地避免这种情况。改进后的代码变为:

//三数取中法
int GetMidIndex(int* a, int left, int right)
{
	int mid = (left + right) / 2;
	if (a[left] < a[mid])
	{
		if (a[right] < a[left])
		{
			return left;
		}
		else if (a[right] > a[mid])
		{
			return mid;
		}
		else
		{
			return right;
		}
	}
	else//a[left] > a[mid]
	{
		if (a[left] < a[right])
		{
			return left;
		}
		else if (a[mid] > a[right])
		{
			return mid;
		}
		else
		{
			return right;
		}
	}

}
//单趟排序
int PartSort1(int* a, int begin, int end)
{
	int midindex = GetMidIndex(a, begin, end);
	Swap(&a[midindex], &a[begin]);

	int key = a[begin];//选取左边做key
	int keyindex = begin;
	while (begin < end)
	{
		while (begin < end && a[end] >= key)
		{
			end--;
		}
		while (begin < end && a[begin] <= key)
		{
			begin++;
		}
		Swap(&a[end], &a[begin]);
	}
	//此时begin == end
	Swap(&a[end], &a[keyindex]);//此处不能跟key交换,因为key是局部变量
	return begin;
}
void QuickSort(int* a, int left , int right)
{
	assert(a);
	if (left >= right)
	{
		return;
	}
	int div = PartSort1(a, left, right);
	//[left,div - 1]  div  [div + 1 , right]
	QuickSort(a, left, div - 1);
	QuickSort(a, div + 1, right);
}

 二、挖坑法

       挖坑法是最主流的快速排序的方法,因为其易于理解。挖坑法,顾名思义,就是要把数据挖出来,假设以最左端位置为坑,把它的值保存在一个变量key内,定义索引变量begin和end,使end不断向左边移动,直至a[end]的值小于key,把它放在坑内,然后begin再向右移动,直至a[begin]的值大于key,把它放在a[end]的坑内。由此循环往复,我们可以得到一个与上面排序相同的结果。

程序源代码

/挖坑法
int PartSort2(int* a, int begin, int end)
{
	int midindex = GetMidIndex(a, begin, end);
	Swap(&a[midindex], &a[begin]);
	
	int key = a[begin];//坑
	while (begin < end)
	{
		while (begin < end && a[end] >= key)
		{
			end--;
		}
		a[begin] = a[end];
		while (begin < end && a[begin] <= key)
		{
			begin++;
		}
		a[end] = a[begin];
	}
	//begin == end
	a[begin] = key;
	return begin;
}
void QuickSort(int* a, int left , int right)
{
	assert(a);
	if (left >= right)
	{
		return;
	}
	int div = PartSort3(a, left, right);
	//[left,div - 1]  div  [div + 1 , right]
	QuickSort(a, left, div - 1);
	QuickSort(a, div + 1, right);
}

注意事项

在移动begin和end的时候,同样要每次判断begin是否小于end,防止begin和end错过

三、双指针法

双指针法相对来说较难理解一点。我们要取最后一个元素为key,定义两个变量cur,prev,其中cur是数组的首元素的索引(begin),prev位于数组首元素的前一个位置,即begin - 1。算法的思想是:cur向右不断移动,直至找到a[cur] < key,++prev,然后让prev所在位置与cur所在位置的值进行交换。依次重复这个过程,我们也可以得到跟上面两种方法相同的结果。

程序源代码

//双指针法
int PartSort3(int* a, int begin, int end)
{
	int midindex = GetMidIndex(a, begin, end);
	Swap(&a[midindex], &a[begin]);

	int key = a[end];
	int cur = begin;
	int prev = begin - 1;
	while (cur <= end)
	{
		while (cur<= end && a[cur] >= key)//注意!!!
		{
			cur++;
		}
		if (cur <= end)
		{
			prev++;
			Swap(&a[cur], &a[prev]);
			cur++;
		}
	}
	Swap(&a[++prev], &a[end]);
	return prev;
}
void QuickSort(int* a, int left , int right)
{
	assert(a);
	if (left >= right)
	{
		return;
	}
	int div = PartSort3(a, left, right);
	//[left,div - 1]  div  [div + 1 , right]
	QuickSort(a, left, div - 1);
	QuickSort(a, div + 1, right);
}

代码也可以换一种形式来呈现:

//双指针法
int PartSort3(int* a, int begin, int end)
{
	
	int prev = begin - 1;
	int cur = begin;
	int keyindex = end;
	while (cur <= end)
	{
		if (a[cur] < a[keyindex] && a[cur] != a[keyindex])
		{
			Swap(&a[cur], &a[++prev]);
		}
		cur++;
	}
	Swap(&a[keyindex], &a[++prev]);

	return prev;
}
void QuickSort(int* a, int left , int right)
{
	assert(a);
	if (left >= right)
	{
		return;
	}
	int div = PartSort3(a, left, right);
	//[left,div - 1]  div  [div + 1 , right]
	QuickSort(a, left, div - 1);
	QuickSort(a, div + 1, right);
}

注意事项

在初始化prev的值时,不能将其初始化为-1,要初始化为begin-1。初始化为-1,会导致子数列在进行递归时出现问题。 


今天的分享到这就结束了,欢迎持续关注,有问题可以私信交流~

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

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

相关文章

第五站:C++的内存解析

目录 C内存分布 变量的四种存储方式 函数返回值使用指针(指针函数) 动态分配内存空间 不能使用外部函数的普通局部变量的地址 通过指针函数返回静态局部变量的地址 动态内存 根据需要分配内存,不浪费(根据用户的需求设置内存的容量) 被调用函数之外需要使用被调用函数内…

C# Cad2016二次开发选择文本信息导出(六)

//选文本信息导出 [CommandMethod("getdata")] public void getdata() {// 获取当前文档和数据库Document doc Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;Database db doc.Database;Editor ed doc.Editor;// 获取当前…

亲手打造一个本地LLM语音助手来管理智能家居

经历过 Siri 和 Google 助手之后&#xff0c;我发现尽管它们能够控制各种设备&#xff0c;但却无法进行个性化定制&#xff0c;并且不可避免地依赖于云服务。出于对新知识的渴望以及想在生活中使用一些酷炫的东西&#xff0c;我下定决心&#xff0c;要追求更高的目标。我的要求…

我成为开源贡献者的原因竟然是做MySql-CDC数据同步

今年下半年机缘巧合下公司决定搭建自己的数据中台&#xff0c;中台的建设势必少不了数据集成。首先面临的就是数据集成技术选型的问题&#xff0c;按照社区活跃度、数据源适配性、同步效率等要求对市面上几个成熟度较高的开源引擎进行了深度调研。 最终经过内部讨论决定用Apac…

解决虚拟机字体太小的问题

在win11中&#xff0c;安装VMWare软件后&#xff0c;创建好虚拟机&#xff0c;打开终端后&#xff0c;发现终端里显示的字体太小&#xff0c;不方便使用&#xff0c;因此需要修改。 1、打开终端 2、输入"gsettings set org.gnome.desktop.interface text-scaling-factor…

运筹说 第98期|无约束极值问题

上一期我们一起学习了关于非线性规划问题的一维搜索方法的相关内容&#xff0c;本期小编将带大家学习非线性规划的无约束极值问题。 下面&#xff0c;让我们从实际问题出发&#xff0c;学习无约束极值问题吧&#xff01; 一、问题描述及求解原理 1 无约束极值问题的定义 无约…

日志采集传输框架之 Flume,将监听端口数据发送至Kafka

1、简介 Flume 是 Cloudera 提供的一个高可用的&#xff0c;高可靠的&#xff0c;分布式的海量日志采集、聚合和传 输的系统。Flume 基于流式架构&#xff0c;主要有以下几个部分组成。 主要组件介绍&#xff1a; 1&#xff09;、Flume Agent 是一个 JVM 进程&#xf…

SpringBoot异常处理(Whitelabel Error Page和自定义全局异常处理页面)和整合ajax异常处理

SpringBoot异常处理&#xff08;Whitelabel Error Page和自定义全局异常处理页面&#xff09;和整合ajax异常处理 1、springboot自带的异常处理页面Whitelabel Error Page SpringBoot默认的处理异常的机制&#xff1a;SpringBoot 默认的已经提供了一套处理异常的机制。一旦程…

继钱江之后,赛科龙也出自动挡?RA401自动挡曝光

QJ在发动赛921的当天&#xff0c;还有一台闪300搭载了自动挡&#xff0c;当天的热度高的离谱&#xff0c;并且后续也经常有人问&#xff0c;这自动挡啥时候上市等等&#xff0c;相信有很多人都想要一台排量大一点的自动挡摩托车&#xff0c;而最新的消息赛科龙也在开发一台&…

文本分类的一些记录

背景 过去工作中最常遇到的问题就是文本分类和实体抽取的任务。其中文本分类是自然语言处理中最基础的任务&#xff0c;指的是将文本打上特定的类别标签&#xff0c;以做区分和筛选。文本分类主要流程一般是&#xff1a;先预处理文本&#xff0c;再提取特征&#xff0c;最后通…

PDF修改技巧之:如何简单方便的编辑PDF文件?

在当今精通技术的世界中&#xff0c;PDF 的使用已变得普遍&#xff0c;尤其是在商业和教育方面。如果您在审阅 PDF 文件时遇到语法或其他错误怎么办&#xff1f; 尽管 PDF 文件不像 Word 或在线文档那样容易编辑&#xff0c;但借助高级工具&#xff0c;您一定可以进行编辑。 …

鸿蒙 ArkUI - 常用组件和布局

目录 一、组件 1.按钮 2.单选框 3.切换按钮 4.进度条 5.文本 6.文本输入框 二、布局方式 1.线性布局 2.层叠布局 3.弹性布局 4.网格布局 一、组件 ArkUI有丰富的内置组件&#xff0c;包括文本、按钮、图片、进度条、输入框、单选框、多选框等。我们还可以将基础组件…

HCIA基础知识

IP地址、静态路由、动态路由、交换机 OSPF RIP DHCP VLAN ACL NAT OSI TCP/IP UDP TCP 三次握手&#xff0c;四次挥手&#xff0c;报头 什么是网络&#xff1f; 由网络连接设备通过传输介质将网络终端设备连接起来&#xff0c;进行资源共享、信息传递的平台。 OSI七…

【电子取证篇】蘇小沐的电子取证工具合集在线文档

【电子取证篇】蘇小沐的电子取证工具合集在线文档 弄成了在线表格&#xff0c;记得及时保存&#xff1b;工具永远只是辅助&#xff0c;但不要过多依赖自动化&#xff0c;有难度说明可以提升&#xff0c;既要不断学习也要不停思考&#xff0c;知行合一—【蘇小沐】 【腾讯文档…

202405读书笔记|《作家榜名著:宋词三百首(马未都亲笔推荐版)》——绿酒初尝人易醉,一枕小窗浓睡

《作家榜名著&#xff1a;宋词三百首&#xff08;马未都亲笔推荐版&#xff09;》画很美&#xff0c;词也是&#x1f618;&#x1f618;&#xff0c;既廖远又色彩明艳&#xff0c;丰富而丰盈&#xff0c;看的很欢乐的一本书。部分节选如下&#xff1a; 艳溢香融 天遥地远&…

智能搬运机器人作为一种新型的物流技术

随着物流行业的快速发展&#xff0c;货物转运的效率和准确性成为了企业竞争的关键因素之一。智能搬运机器人作为一种新型的物流技术&#xff0c;已经在许多企业中得到了广泛应用。本文将介绍富唯智能智能搬运机器人在物流行业的应用和优势。 在实际应用中&#xff0c;智能搬运机…

如何在Eclipse IDE中安装TestNG插件

目录 使用Eclipse Marketplace安装TestNG插件 通过输入URL安装TestNG 1.点击安装新软件 2.输入URL以安装TestNG 3.遵循正常的安装过程 4.重新启动Eclipse 在Eclipse中安装TestNG插件的视频 在这篇文章中&#xff0c;我们将介绍如何在Eclipse IDE中安装TestNG插件&#x…

基于算术电路的全同态加密方案介绍

基于算术电路的全同态加密方案介绍 摘 要&#xff1a; 云计算技术目前已经发展得相对成熟&#xff0c;应用也逐步得到普及&#xff0c;它所具有的强大的数据处理能力&#xff0c;能够帮助个体用户计算复杂的数据。但它带来便利的同时&#xff0c;也催生了一系列用户隐私数据保…

网络安全全栈培训笔记(53-WEB攻防-通用漏洞CRLF注入URL重定向资源处理拒绝服务)

第53天 WEB攻防-通用漏洞&CRLF注入&URL重定向&资源处理拒绝服务 知识点&#xff1a; 1、CRLF注入-原理&检测&利用 2、URL重定向-原理&检测&利用 3、Web拒绝服务-原理&检测&利用 #下节预告&#xff1a; 1、JSONP&CORS跨域 2、域名安全…

HCIP ISIS实验

拓扑图&IP划分如下图&#xff1a; 第一步&#xff0c;配置IP地址&环回地址 以R1为例&#xff0c;R2~R8同理 interface GigabitEthernet 0/0/0 ip address 18.1.1.1 24 interface GigabitEthernet 0/0/1 ip address 12.1.1.1 24 interface LoopBack 0 ip address 1.1.…