快速排序三种思路详解!

一、快速排序的介绍

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右 子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。(其时间复杂度最优为N*logN,空间复杂度最优为lonN,这里就不予证明了!过程相当复杂!)

用一种通俗的话来讲快速排序其实就时设定一个界定值,然后分别开始遍历需要排序的元素,将小于该界定值和大于该界定值的放在其两侧,每次结束都能将该界定值放到其最终正确的位置!


二、快速排序的思想

快速排序其思想也是采用了分治的思想,将一个大问题转化为若干个小问题,然后逐个解决每个小问题,最终达到解决问题的目的!


三、快速排序的实现

由上面内容可以知道,若想实现快速排序,则可以先执行单个元素的排序,然后再进行递归处理解决整组数据的排序!因为快速排序是一种二叉树结构的交换排序,所以可以采用递归的方法将其解决!

 

下面来看一下如何实现单趟排序,让数据中一个元素位于其最终应处于的位置!

单趟排序的实现

注:本篇文章例子是以默认排升序,若要实现降序仅需要将循环条件改变一下即可!

法一:(霍尔思想)

其单趟排序的思想就是假定一个界定值,然后左右两边开始遍历,若要排升序,则左边找到的比界定值大的值就停下,右边找到比界定值小的就停下,然后交换两者的值,当左右两边相遇时,就结束循环,最后将相遇的位置的值与界定值交换位置即可完成本次界定值的最终位置的实现!

//注意:因为这是单趟排序,保证了界定值处于该数据最终所在的位置,所以该函数应返回本次界定值的位置,方便下次调用这个函数实现递归调用解决其另外界定值最终的位置!

画个图来形象的描述一下该过程是如何实现的吧!

代码如下:

int Hoare(int* a, int left,int right)
{int vali = left;while (left < right){//若没有left<right这个限制条件,那么当从左边开始走的时候其对应的值一直小于a[vali]时,则会导致越界问题!//注意若选取vali在左边,那么从右边先走,因为从右边先走才能保证最后相遇的位置一定是小于vali的值,因为从右边找//的话是找比val小于或等于的值停下来!while (left < right && a[right] >= a[vali]){right--;}while (left < right && a[left] <= a[vali]){left++;}swap(&a[left], &a[right]);}//因为最后left和right相遇,所以只需将a[vali]与二者任意一个交换位置即可!swap(&a[left], &a[vali]);return left;
}

注意:(1)该函数的实现需要注意的是,必须保证里面的循环条件是left<right,因为假设当从左边开始时,其后面的值一直小于等于val时,则会导致left越界,进而导致程序错误!

(2)还需要注意一点的是,当选的界定值在左边时,那么必须从右边开始遍历,因为从右边先走可以保证最终左边相遇右边时,其遇到的右边一定是小于val的值,因为当最后左边和右边相遇时,还需要交换左边或右边的值与val的值,若交换的值大于val时,交换之后则没有排序成功,反之,若选的界定值在右边时,那么必须从左边开始遍历,原理同上!

法二(挖坑大法!)

原理如下:挖坑大法的原理就是选定一个界定值,将其保存起来,然后将其位置假设为一个坑,然后左右两边分别开始遍历,当左边找到比界定值大的值时,则将其数据填入坑中,坑的位置更新为新填入数据原来的位置,当右边位置找到比界定值小的值时,也将其数据填入坑中,坑的位置更新为新填入数据原来的位置!最后当左右相遇时,它们相遇一定是在坑中相遇,将原来界定值存放到坑中,此时界定值与其左右两边数据保持相对有序!图像如下:

注:这里假定坑位为左边第一个数据!那么就必须从右边开始先遍历,原因与法一相同!

 

 

代码如下:

int hole (int* a, int left, int right)
{int hole = left;int val = a[left];while (left < right){//先从右边开始找比val小的值,找到后把坑填了,然后其位置就成为新的坑位!while (left < right && a[right] >= val){right--;}a[hole ] = a[right];hole = right;while (left < right && a[left] <= val){left++;}a[hole ] = a[left];hole = left;}a[hole ] = val;return left;
}

 法三:双指针

原理:定义两个快慢指针,当快指针指向的值小于界定值时,让慢指针向后走一步,然后交换快慢指针对应的值,不管如何快指针是都要向后走的,只有当快指针的值小于val时,慢指针才会向后走一步,然后与其值交换!最后当快指针走完整个数据循环结束!从其思想上面可以看出,当快慢指针之间的值都是比界定值大的数据,因为只有当指针指向的值小于界定值时,慢指针才会向后走,并与当前快指针指向的小于界定值的数据与前面的慢指针的值进行交换!

图像如下:

从图中可以看出,fast和slow之间的值都是大于界定值的!所以当fast找到比界定值小的数值后,进行slow++操作,然后交换二者位置,仍然可以保证 fast和slow之间的值还都是大于界定值!

代码如下:

int Dpointer(int* a, int left, int right)
{int slow = left;int fast = left + 1;int vali = left;while (fast<=right){if (fast!=++slow && a[fast] < a[vali])//当slow++的位置和fast位置相同时就没必要进行交换了!{slow++;swap(&a[fast], &a[slow]);}fast++;}//注意一定要交换slow当前的值与原vali的值,然后将vali的位置重新赋值为slow的位置!swap(&a[slow], &a[vali]);vali = slow;return vali;
}

以上三种方法都是单趟的排序,因为快排是一种二叉树结构的交换排序方法,所以要想将所有元素进行排序,就可以采用递归的方法进行操作,从而完成整组数据的排序!

整组数据的排序实现!

代码如下:

void Qsort(int* a, int left, int right)
{//控制当区间不存在时,则跳出递归!if (left >= right){return;}/*int vali = Hoare(a, left,right);*///int vali = hig(a, left,right);int vali = Dpointer(a, left, right);Qsort(a, left, vali - 1);Qsort(a, vali+1, right);
}

当第一个排过序后,其第一个界定值就处于其最终存在的位置,所以可将界定值左边的元素再次进行一次排序,从而找到下一个界定值最终处于的位置,直至最后所要排序的区间不存在,排序即完成!

 

今日的快排分享到此为止,如有小伙伴还有不懂的地方欢迎在评论区留言提问哦! 

 

 

 

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

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

相关文章

激活函数总结(十九):激活函数补充(ISRU、ISRLU)

激活函数总结&#xff08;十九&#xff09;&#xff1a;激活函数补充 1 引言2 激活函数2.1 Inverse Square Root Unit &#xff08;ISRU&#xff09;激活函数2.2 Inverse Square Root Linear Unit (ISRLU)激活函数 3. 总结 1 引言 在前面的文章中已经介绍了介绍了一系列激活函…

python AI绘图教程

前提 1.安装python 2.安装git 步骤 下载stable-diffusion-webui项目&#xff08;链接&#xff1a;GitHub - AUTOMATIC1111/stable-diffusion-webui: Stable Diffusion web UI&#xff09; git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git 安装st…

商城-学习整理-高级-消息队列(十七)

目录 一、RabbitMQ简介(消息中间件)1、RabbitMQ简介&#xff1a;2、核心概念1、Message2、Publisher3、Exchange4、Queue5、Binding6、Connection7、Channel8、Consumer9、Virtual Host10、Broker 二、一些概念1、异步处理2、应用解耦3、流量控制5、概述 三、Docker安装RabbitM…

【C++ 学习 ⑰】- 继承(下)

目录 一、派生类的默认成员函数 二、继承与友元 三、继承与静态成员 四、复杂的菱形继承及菱形虚拟继承 五、继承和组合 一、派生类的默认成员函数 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认构造函数&#xff0c;那么必须在派生…

报错sql_mode=only_full_group_by

首发博客地址 https://blog.zysicyj.top/ 报错内容 ### The error may exist in file[D:\code\cppCode20221025\leader-system\target\classes\mapper\system\TJsonDataMapper.xml] ### The error may involve defaultParameterMap ### The error occurred while…

如何使用LLM实现文本自动生成视频

推荐&#xff1a;使用 NSDT场景编辑器 助你快速搭建可二次编辑的3D应用场景 介绍 基于扩散的图像生成模型代表了计算机视觉领域的革命性突破。这些进步由Imagen&#xff0c;DallE和MidJourney等模型开创&#xff0c;展示了文本条件图像生成的卓越功能。有关这些模型内部工作的…

【C++】UDP通信,实现文件的传输

目录 1 TCP与UDP比较 2 UDP 3 通信流程 4 实践 5 运行结果 1 TCP与UDP比较 2 UDP简介 UDP通信是无连接的,因此不需要

Spring与Mybatis集成且Aop整合(放飞双手,迅速完成CRUD及分页)

目录 一、概述 二、集成 ( 1 ) 为什么 ( 2 ) 优点 ( 3 ) 实例 三、整合 3.1 讲述 3.2 整合进行分页 带我们带来的收获 一、概述 集成是指将不同的组件、系统或框架整合在一起&#xff0c;使它们能够协同工作&#xff0c;共同完成某个功能或提供某种服务。在软件开发中&…

C语言之三子棋游戏实现篇

目录 主函数test.c 菜单函数 选择实现 游戏函数 &#xff08;函数调用&#xff09; 打印棋盘数据 打印展示棋盘 玩家下棋 电脑下棋 判断输赢 循环 test.c总代码 头文件&函数声明game.h 头文件的包含 游戏符号声明 游戏函数声明 game.h总代码 游戏函数ga…

服务器中了mkp勒索病毒该怎么办?勒索病毒解密,数据恢复

mkp勒索病毒算的上是一种比较常见的勒索病毒类型了。它的感染数量上也常年排在前几名的位置。所以接下来就由云天数据恢复中心的技术工程师来对mkp勒索病毒做一个分析&#xff0c;以及中招以后应该怎么办。 一&#xff0c;中了mkp勒索病毒的表现 桌面以及多个文件夹当中都有一封…

Linux:shell脚本:基础使用(6)《正则表达式-awk工具》

简介 awk是行处理器: 相比较屏幕处理的优点&#xff0c;在处理庞大文件时不会出现内存溢出或是处理缓慢的问题&#xff0c;通常用来格式化文本信息 awk处理过程: 依次对每一行进行处理&#xff0c;然后输出 1&#xff09;awk命令会逐行读取文件的内容进行处理 2&#xff09;a…

Neo4j实现表字段级血缘关系

需求背景 需要在前端页面展示当前表字段的所有上下游血缘关系&#xff0c;以进一步做数据诊断治理。大致效果图如下&#xff1a; 首先这里解释什么是表字段血缘关系&#xff0c;SQL 示例&#xff1a; CREATE TABLE IF NOT EXISTS table_b AS SELECT order_id, order_status F…

PB4引脚作GPIO上电高电平问题

问题说明 给旧项目debug&#xff0c;芯片是国民技术 N32G452VEL7 &#xff08;用起来跟32没多大差 包括PB4在内有多个引脚作为输出&#xff0c;默认低电平&#xff0c;在状态机内先输出高电平再回到低电平&#xff0c;来模拟按键的状态&#xff08;相当于按键按下松开后按键功…

计算机竞赛 基于大数据的时间序列股价预测分析与可视化 - lstm

文章目录 1 前言2 时间序列的由来2.1 四种模型的名称&#xff1a; 3 数据预览4 理论公式4.1 协方差4.2 相关系数4.3 scikit-learn计算相关性 5 金融数据的时序分析5.1 数据概况5.2 序列变化情况计算 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &…

词向量及文本向量

文章目录 引言1. 文本向量化2. one-hot编码3. 词向量-word2vec3.1 词向量-基于语言模型 4 词向量 - word2vec基于窗口4.1 词向量-如何训练 5. Huffman树6. 负采样-negative sampling7. Glove基于共现矩阵7.1 Glove词向量7.2 Glove对比word2vec 8. 词向量训练总结9. 词向量应用9…

论文解读 | ScanNet:室内场景的丰富注释3D重建

原创 | 文 BFT机器人 大型的、有标记的数据集的可用性是为了利用做有监督的深度学习方法的一个关键要求。但是在RGB-D场景理解的背景下&#xff0c;可用的数据非常少,通常是当前的数据集覆盖了一小范围的场景视图&#xff0c;并且具有有限的语义注释。 为了解决这个问题&#…

9.阿里Sentinel哨兵

1.Sentinel Sentinel&#xff08;哨兵&#xff09;是由阿里开源的一款流量控制和熔断降级框架&#xff0c;用于保护分布式系统中的应用免受流量涌入、超载和故障的影响。它可以作为微服务架构中的一部分&#xff0c;用于保护服务不被异常流量冲垮&#xff0c;从而提高系统的稳定…

系统上线安全测评需要做哪些内容?

电力信息系统、航空航天、交通运输、银行金融、地图绘画、政府官网等系统再正式上线前需要做安全测试。避免造成数据泄露从而引起的各种严重问题。 那么系统上线前需要做哪些测试内容呢&#xff1f;下面由我给大家介绍 1、安全机制检测-应用安全 身份鉴别 登录控制模块 应提供…

Linux:权限

目录 一、shell运行原理 二、权限 1.权限的概念 2.文件访问权限的相关设置方法 三、常见的权限问题 1.目录权限 2.umsk(权限掩码) 3.粘滞位 一、shell运行原理 1.为什么我们不是直接访问操作系统&#xff1f; ”人“不善于直接使用操作系统如果让人直接访问操作系统&a…

数据通信——TCP(三次握手及基础特性)

引言 TCP&#xff08;传输控制协议&#xff09;&#xff0c;不像之前的UDP那样&#xff0c;因为这个协议要将很多复杂的东西&#xff0c;所以这次的特性是简单的特性&#xff0c;后续会讲一些复杂难懂的知识&#xff0c;这次先说一些TCP明显的特性 面向连接 TCP提供了对连接的管…