快排(前后指针实现)

news2024/11/27 10:32:10

02f9ea45b5174b05b8ad7dad4f45fe9d.png

前言

快排解决办法有很多种,这里我再拿出来一种前后指针版本

虽然这个版本的时间复杂度和霍尔一样,逻辑也差不多,但是实际排序过程,确实会比霍尔慢一点

快排gif

b3d521cc988c4a3193d21d0c0e3e8b5a.gif

快排前后指针实现逻辑:

前后指针实现逻辑(升序):
单趟排序:
1,我们使用递归来进行实现,所以我们先实现单趟排序
2,定义两个下标,也就是所谓的指针,初始的时候,一个指向最左边第一个元素(prev),一个指向第二个元素(cur),最后定义一个对比keyi3,此时先判断我们的cur是不是小于keyi。cur小于keyi的话:prev++,交换,之后cur++4,但是我们如果和自己交换此时没有什么意义,所以这里我们需要做一个处理
5,继续往前走,如果遇见的是:比keyi下标大的元素此时,cur++,直到遇见的是比keyi下标小的元素,循环执行.prev++,交换,之后cur++

6,最后cur走到最后一个元素,我们交换keyi的下标元素和prev的下标元素

多趟实现:
1,递归进行分割:【left,keyi-1】keyi【keyi+1,right】
2,停止条件就是当left>=right
原因:【left, keyi-1】keyi【keyi+1, right】

07b544bcff274870887ae4d9edb5c0a3.png

快排单趟实现

这里只是图解单趟实现逻辑,因为多趟实现的逻辑和霍尔的一样,也是递归,所以不进行多次书写

c09ef1f8aec043a7bdcfa20e170442b0.png

代码实现

这里的代码实现的核心逻辑不一样,大的框架是一样的,也就是三数取中,以及减少递归从而使用插入排序这样的逻辑是一样的,下面我们来看看这个新的组装怪

//快排(前后指针方法)(递归实现)
void QuickSort02(int* a, int left, int right)
{
	//递归停止条件
	if (left >= right)
		return;
	//创建两个变量,作为前后指针使用
	int prev = left; int cur = prev + 1;
	int keyi = left;
	//当快指针到尾的时候,跳出循环->交换
	while (cur <= right)
	{
		//前后指针中间是比a[keyi]大的数值,所以遇见大的++,小的停止
		if (a[keyi] > a[cur])
		{
			//停止之后,慢指针++,并且进行交换,因为中间才是大的数值,cur遇见大数值++。遇见小数值才停下来
			prev++;
			Swap(&a[prev], &a[cur]);
			//同理快指针也进行++,往后移动
			cur++;
		}
		else
		{
			cur++;
		}
	}
	Swap(&a[prev], &a[keyi]);
	keyi = prev;
	//多趟递归实现
	//[left,keyi-1] keyi [keyi+1,right]   这里传递的是区间
	//  1     0      1     2      1       当只剩一个数值的时候,也就是这个区间的时候,递归停止 
	QuickSort02(a, left, keyi - 1);
	QuickSort02(a, keyi + 1, right);
}

总结:

  1. 算法基础:快速排序是一种分而治之的排序算法,它通过递归地将数组分为较小的子数组,然后对这些子数组进行排序。

  2. 分区策略:使用前后指针(prevcur)进行分区,而不是传统的左右指针。这种方法在某些情况下可以减少元素交换的次数。

  3. 基准值选择:基准值(keyi)是数组的第一个元素,即left索引对应的元素。

  4. 指针移动规则

    • prev作为慢指针,用于跟踪比基准值小的元素的边界。
    • cur作为快指针,从left + 1开始遍历数组。
  5. 元素交换:当快指针指向的元素大于基准值时,不进行交换,快指针继续移动;当快指针指向的元素小于或等于基准值时,与慢指针所指元素交换,然后慢指针和快指针都向前移动。

  6. 递归排序:在基准值确定之后,递归地对基准值左边和右边的子数组进行排序。

  7. 时间复杂度:平均情况下,快速排序的时间复杂度为O(n log n)。最坏情况下,如果每次分区都极不平衡,时间复杂度会退化到O(n^2)。

  8. 空间复杂度:由于递归性质,快速排序的空间复杂度为O(log n)。

  9. 算法优化:通过前后指针方法,可以在某些情况下提高快速排序的性能,特别是当基准值接近数组中间值时。

  10. 适用场景:快速排序适用于大多数需要排序的场景,特别是在大数据集上,它通常能够提供非常高效的排序性能。

优化

53718a8efe964eeb9fd6b68d66153777.png

这里我们可以看到,cur无论是if还是else里面都需要++,所以我们直接放到外面

其次我们为了效率,不和自己交换,我们不和自己交换,进行一个判断条件

快慢指针代码优化(完整)

//交换函数
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
//快排(前后指针方法)(递归实现)
void QuickSort02(int* a, int left, int right)
{
	//递归停止条件
	if (left >= right)
		return;
	if (right - left + 1 >= 10)
	{
		InsertionSort(a + left, right - left + 1);
	}
	else
	{
		//三数取中
		int mid = GetMid(a, left, right);
		Swap(&a[mid], &a[left]);

		//单趟实现
		//创建两个变量,作为前后指针使用
		int prev = left; int cur = prev + 1;
		int keyi = left;
		//当快指针到尾的时候,跳出循环->交换
		while (cur <= right)
		{
			//前后指针中间是比a[keyi]大的数值,所以遇见大的++,小的停止
			if (a[keyi] > a[cur] && prev++ != cur)
			{
				//停止之后,慢指针++,并且进行交换,因为中间才是大的数值,cur遇见大数值++。遇见小数值才停下来
				Swap(&a[prev], &a[cur]);
			}
			cur++;
		}
		Swap(&a[prev], &a[keyi]);
		keyi = prev;
		//多趟递归实现
		//[left,keyi-1] keyi [keyi+1,right]   这里传递的是区间
		//  1     0      1     2      1       当只剩一个数值的时候,也就是这个区间的时候,递归停止 
		QuickSort02(a, left, keyi - 1);
		QuickSort02(a, keyi + 1, right);
	}
}

总结:

  1. 基本递归结构:算法使用递归调用来处理子数组,这是快速排序算法的核心结构。

  2. 小数组优化:当子数组的大小小于或等于10时,算法使用插入排序而不是快速排序,因为插入排序在小规模数据上更高效。

  3. 三数取中法:为了更均匀地分割数组,算法使用三数取中法选择基准值,这有助于减少最坏情况发生的概率。

  4. 前后指针方法:算法使用两个指针(prevcur),其中prev作为慢指针,cur作为快指针,通过这种方式进行一次遍历完成分区。

  5. 分区操作:快指针从left + 1开始遍历,如果当前元素小于基准值,则与慢指针所指的元素交换,然后慢指针向前移动。

  6. 递归排序子数组:基准值确定后,算法递归地对基准值左边和右边的子数组进行排序。

  7. 时间复杂度:平均情况下,算法的时间复杂度为O(n log n),最坏情况下为O(n^2)。但由于采用了小数组优化和三数取中法,最坏情况的发生概率降低。

  8. 空间复杂度:算法的空间复杂度为O(log n),这主要由于递归调用导致的栈空间使用。

  9. 适用场景:这种改进的快速排序算法适用于大多数需要排序的场景,尤其是在大数据集上,它能够提供非常高效的排序性能,同时在小数据集上也表现出较好的效率。

  10. 算法稳定性:由于使用了插入排序对小规模子数组进行排序,算法在处理小规模数据时具有稳定性。

  11. 注意:在实际测试·里面,前后指针比霍尔排序慢一点

通过这种混合排序策略,算法在保持快速排序优点的同时,也提高了在特定情况下的排序效率,使其成为一种更加健壮的排序方法。

注意事项

这里调用的插入排序是升序,写的快排也是升序,如果你需要测试降序,那么你需要把插入排序一起改成降序,不然会导致排序冲突

 

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

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

相关文章

双写一致性

双写一致性 当修改了数据库的数据也要同时更新缓存的数据&#xff0c;缓存和数据库的数据要保持一致。 注意这里是对数据库进行写操作而不是读操作&#xff0c;通常我们有两种方式完成这个写操作&#xff0c;分别是&#xff1a;先删除缓存再修改数据库 和 先修改数据库再删除…

视觉与运动控制1

机器视觉与运动控制 机器视觉的应用 机器视觉是计算机视觉的一个分支&#xff0c;工业自动化领域中的视觉控制或视觉应用&#xff0c;主要指的是通过前端光学摄像头&#xff0c;从采集到的数字化图像中提取相关信息&#xff0c;进行分析处理之后&#xff0c;用于对生产线流程…

编译xlnt开源库源码, 使用c++读写excel文件

编译xlnt开源库源码,在linux平台使用c读写excel文件 下载xnlt源码 官方网站https://tfussell.gitbooks.io/xlnt/content/ 下载地址https://github.com/tfussell/xlnt 下载libstudxml开源库源码 下载地址https://github.com/kamxgal/libstudxml 下载xnlt源码 官方网站https://…

AI网络爬虫:用deepseek提取百度文心一言的智能体数据

真实网址&#xff1a;https://agents.baidu.com/lingjing/experhub/search/list?pageSize36&pageNo1&tagId-99 返回的json数据&#xff1a;{ "errno": 0, "msg": "success", "data": { "total": 36, "p…

【动态规划】路径问题 {二维动态规划;选择合适的状态表示方法;创建虚拟节点}

一、经验总结 选择合适的状态表示方法 一般的&#xff0c;状态表示的方法有两种&#xff1a; 以[i, j]位置为终点&#xff0c;正向填表&#xff1b;用之前的状态推导出dp[i][j]的值&#xff08;从哪里来&#xff09;&#xff1b;以[i, j]位置为起点&#xff0c;反向填表&…

Spring Boot组件化与参数校验

Spring Boot组件化与参数校验 Spring Boot版本选择 2.3.x版本 2.6.x版本 Spring Boot核心思想 约定大于配置&#xff0c;简化繁琐的配置 Spring Boot自动配置原理 SpringBootApplication: Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类&#xff0c;Spr…

详细分析Oracle日期和时间的基本命令

目录 1. 基本类型2. 常用函数3. Demo 1. 基本类型 Oracle支持不同的日期格式模型&#xff0c;其中包括&#xff1a; ISO 8601: YYYY-MM-DDTHH:MI:SS&#xff0c;例如2024-06-20T14:30:00Oracle内部格式: DD-MON-YYYY HH:MI:SS AM&#xff0c;例如20-JUN-2024 02:30:00 PM DA…

6月20日(周四)欧美股市总结:“三巫日”当前,标普开盘创新高后与纳指转跌,英伟达一度跳水8%,市值跌离最大

美国上周首次申请失业救济人数高于预期&#xff0c;新屋建造和费城制造业数据均显示经济放缓&#xff0c;市场维持对美联储年内降息两次的预期。标普盘初升破5500点创新高后下跌&#xff0c;纳指止步七日连创新高&#xff0c;但道指涨300点至四周新高。英伟达盘初涨3.8%至盘中最…

【C++高阶】探索STL的瑰宝 map与set:高效数据结构的奥秘与技巧

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;初步了解 二叉搜索树 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀map与set &#x1f4d2;1.…

绿色领航·数链未来“2024中国消费电子博览会”招商工作全面启动

中国国际消费电子博览会&#xff08;简称CICE电博会&#xff09;自2001年创办以来&#xff0c;已逐渐发展成为全球极具影响力的行业盛会。它不仅是国内外消费电子产业的重要交流平台&#xff0c;更是展示我国消费电子产业发展成果的重要窗口。2024年&#xff0c;这一盛会再次在…

windows下前端开发环境安装

文章目录 windows下前端开发环境安装1. Cmder 终端使用1.1 cmder进入指定目录 2. nodejs环境安装3. vscode编辑器下载3.1 vscode插件离线安装 windows下前端开发环境安装 1. Cmder 终端使用 使用Cmder替换cmd&#xff0c;让开发更高效 https://cmder.net/ 打开网址后&#…

系统架构设计师 - 数据库系统(1)

数据库系统 数据库系统数据库模式 ★分布式数据库 ★★★数据库设计阶段 ★★ER模型 ★关系模型 ★ ★结构约束条件完整性约束 关系代数 ★ ★ ★ ★概述自然连接 大家好呀&#xff01;我是小笙&#xff0c;本章我主要分享系统架构设计师 - 数据库系统(1)知识&#xff0c;希望内…

掌握心理学知识成为产品经理一门必修课?

文章目录 心理学与产品设计的关联关系产品经理需要学习哪些心理学知识产品心理学的学习对象包含哪些 谈及心理学&#xff0c;往往认为它是一门研究人类心理现象及其影响下的精神功能和行为活动的科学&#xff0c;很多情况下&#xff0c;我们的直观印象是把心理学与医学领域进行…

Hadoop三大组件原理详解:hdfs-yarn-MapReduce(第9天)

系列文章目录 一、HDFS读写原理【重点】 二、YARN提交mr流程【重点】 三、MapReduce计算流程【重点】 文章目录 系列文章目录前言一、HDFS读写原理[面试]1、HDFS数据写入解析2、HDFS数据读取解析 二、YARN提交mr流程[面试]1. YARN提交mr过程解析 三、MapReduce计算流程[面试]1…

RK3568技术笔记十四 Ubuntu创建共享文件夹

单击“虚拟机”&#xff0c;单击“设置”&#xff0c;如图所示&#xff1a; 单击“选项”&#xff0c;选择“总是启用&#xff08;E&#xff09;”&#xff0c;单击“添加”&#xff0c;如图所示&#xff1a; 单击“下一步”&#xff0c;如图所示&#xff1a; 单击“浏览”添加…

建筑幕墙设计乙级资质申请要点梳理

建筑幕墙设计乙级资质申请要点梳理如下&#xff1a; 一、申请条件 资历和信誉&#xff1a; 企业需具有独立企业法人资格&#xff0c;能够独立承担法律责任。社会信誉良好&#xff0c;无不良记录。注册资本不少于100万元人民币。技术条件&#xff1a; 专业配备齐全、合理&#…

苹果电脑如何清理磁盘空间 苹果电脑如何清理系统数据

你是否遇到过电脑磁盘空间不足的情况呢&#xff1f;Mac电脑有着流畅的操作系统&#xff0c;但是随着日常使用&#xff0c;可能电脑里的垃圾文件越来越多&#xff0c;导致磁盘空间不足&#xff0c;随之会出现电脑卡顿、软件闪退等情况。及时清理磁盘空间可以有效避免电脑这些问题…

java连接kerberos用户认证

文章目录 一、背景二、代码2.1目录2.2配置文件application.properties2.3pom依赖2.4代码AuthProviderConfig配置类CustomConfigurationByKeytab配置类CustomConfigurationByPassword配置类TestControllerMyCallbackHandlerDummyUserDetailsService实现类LdapTest2Application启…

AI播客下载:Possible(OpenAI投资人Reid Hoffman主持)

这是由 LinkedIn 创办人&#xff0c;同时也是 OpenAI 早期投资者 Reid Hoffman。节目上会邀请各领域的专家 (教育、医疗、司法)&#xff0c;探讨 AI 对世界的影响。这节目特别有趣的地方&#xff0c;是在 GPT-4 协助下制作的&#xff0c;开创了人机协作内容 。 如果未来一切都能…

Windows10任务栏卡顿解决方案

一、重新启动任务资源管理器 右键底部任务栏选择“任务管理器”&#xff1b;按快捷键“CtrlShiftEsc”&#xff1b;搜索框搜索“任务管理器”并单击“打开”&#xff1b;“WinX”打开开始菜单附属菜单&#xff0c;在列表中选择“任务管理器” &#xff1b;按下“ctrlaltdelete”…