【数据结构】排序——快速排序
前言
本篇博客我们继续介绍一种排序——快速排序,让我们看看快速排序是怎么实现的
💓 个人主页:小张同学zkf
⏩ 文章专栏:数据结构
若有问题 评论区见📝
🎉欢迎大家点赞👍收藏⭐文章


目录
1.快速排序(hoare方法)
2.快速排序(挖坑法)
3.快速排序(前后指针法)
4.快速排序(非递归法)
5.快速排序特性
1.快速排序(hoare方法)
// 假设按照升序对 array 数组中 [left, right) 区间中的元素进行排序void QuickSort ( int array [], int left , int right ){if ( right - left <= 1 )return ;// 按照基准值对 array 数组的 [left, right) 区间中的元素进行划分int div = partion ( array , left , right );// 划分成功后以 div 为边界形成了左右两部分 [left, div) 和 [div+1, right)// 递归排 [left, div)QuickSort ( array , left , div );// 递归排 [div+1, right)QuickSort ( array , div + 1 , right );}
我们先看看快速排序的动图

整体思想,以左面的数据为key,然后先让right指针向右走,找比key位置上的值小的值,找到之后,停止移动,然后left指针向左移动找比key大的值,找到之后,交换left和right位置上的值,然后右指针继续找小,左指针继续找大,找到之后继续交换,重归此过程,直到左指针与右指针相遇,相遇的位置与key位置上的值交换,再把key赋值成相遇的位置。这是单趟排序。再将以key为中心分成两个左右区间再次递归到这个函数中,不断递归,直到最后的区间为1,或不存在区间。递归返回。


代码如下

但如果我们想让快排效率高,我们得考虑些极端情况,比如如果右边指针一直没找到比最左边的数大的,左右指针直接在key位置上相遇了。 递归只有一个区间一直递归,就会大大降低了快排的效率,特别是在有序的情况下,所以,只有每次递归,key都在中间位置时,效率才最快,所以我们可以定义一个三数取中的函数,函数的返回值与left位置上的值交换就ok了。

那三数取中么写,其实很简单,就是比较最左边最右边以及最中间的值,谁是第二大的,返回第二大的就行。
三数取中代码如下
int sanshuquzhong(int* a,int left, int right)
{int mid = (left + right) / 2;if (a[left] >a [mid]){if (a[mid]>a[right]){return mid;}else{if (a[right] > a[left]){return left;}else{return right;}}}else{if (a[mid] < a[right]){return mid;}else{if (a[right] > a[left]){return right;}else{return left;}}}
}
有了三数取中,快排效率就明显提高,但是还是有人觉得快排不够快,确实,随着递归的深入,效率会越来越慢,所以为了加快效率,我们可以进行小区间优化
我们由图分析可知最后一次递归耗费次数最多,所以我们可以对最后几次小区间下手,用其他排序替换快排,从而让效率提高,我们可以在最后几个区间时用插入排序来进行
void charupaixu(int* a, int n)
{for (int i = 0; i < n - 1; i++){int end = i;int tmp = a[end + 1];while (end >= 0){if (a[end] > tmp){a[end + 1] = a[end];end--;}else{break;}}a[end + 1] = tmp;}
}
ok,到这里我们的代码就写完了,我们想一个问题,为什么我们要选key,并且选的key在左边时,一定要右边指针先走才行,为什么这么规定那。如下图分析
这样快速排序(hoare方法)就初步得成,所有代码如下
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void swap(int* as, int* ak)
{int tmp = *as;*as = *ak;*ak = tmp;
}
void charupaixu(int* a, int n)
{for (int i = 0; i < n - 1; i++){int end = i;int tmp = a[end + 1];while (end >= 0){if (a[end] > tmp){a[end + 1] = a[end];end--;}else{break;}}a[end + 1] = tmp;}
}
int sanshuquzhong(int* a,int left, int right)
{int mid = (left + right) / 2;if (a[left] >a [mid]){if (a[mid]>a[right]){return mid;}else{if (a[right] > a[left]){return left;}else{return right;}}}else{if (a[mid] < a[right]){return mid;}else{if (a[right] > a[left]){return right;}else{return left;}}}
}
void kuaisupaixu(int* arr, int left,int right)
{if (right <= left){return;}if (right - left + 1 < 10)//小区间排序{charupaixu(arr + left, right -left+ 1);}int mid = sanshuquzhong(arr,left, right);//三数取中swap(&arr[mid], &arr[left]);int key = left;int begin = left;int end = right;while (begin<end){while (begin<end&&arr[end] >=arr[key]){end--;}while (begin<end&&arr[begin] <= arr[key]){begin++;}swap(&arr[end], &arr[begin]);}swap(&arr[begin], &arr[key]);key = begin;kuaisupaixu(arr,left,key-1);kuaisupaixu(arr,key+1,right);
}
2.快速排序(挖坑法)
随着快排的不断发展,人们优化了hoare方法,用挖坑法,虽然这种方法没有效率的提升,不过方便了人们对代码的理解再也不用考虑为什么要右边先走的问题
我们看一下这个方法的动图

其实就是把交换换成填补,定义一个临时变量为坑,最后把Key自然放进坑位就行,这个方法更方便我们理解
就是在hoare方法代码中微调一下就行
代码如下
// 快速排序挖坑法
void PartSort2(int* a, int left, int right)
{if (left >= right){return;}if (right - left + 1 < 10){charu(a+left, right - left + 1);}else{int mid = sanshuquzhong(a, left, right);swap(&a[mid], &a[left]);int key = a[left];int begin = left;int end = right;int keng = left;while (begin < end){while (begin < end && a[end] >= key){end--;}a[keng] = a[end];keng = end;while (begin < end && a[begin] <= key){begin++;}a[keng] = a[begin];keng = begin;}a[begin] = key;PartSort2(a, left, begin- 1);PartSort2(a, begin + 1, right);}}
3.快速排序(前后指针法)
快速排序还有另一种方法,也是最容易记住的,我们可以通过定义两个指针,刚开始一个指向key,一个指向key的下一个数,让前面那个指针一直向前走找比key小的数,第二个若找到比key小的数,那么前后指针之间的数就是比key大的数,++后指针再交换俩指针指向的数,前指针继续向前找,直到超过边界停止,最后key与此时后指针指向的书交换,并且key赋值于后指针的位置,递归key前key后空间
动图如下

我们可以画图分析一下
代码如下
// 快速排序前后指针法
void PartSort3(int* a, int left, int right)
{if (left >= right){return;}if (right - left + 1 < 10){charu(a + left, right - left + 1);}else{int mid = sanshuquzhong(a, left, right);swap(&a[mid], &a[left]);int key = left;int man = left;int kuai = left + 1;while (kuai <= right){if (a[kuai] < a[key] && ++man != kuai){swap(&a[man], &a[kuai]);}kuai++;}swap(&a[key], &a[man]);key = man;PartSort3(a, left, key - 1);PartSort3(a, key + 1, right);}
}
4.快速排序(非递归法)
前三种方法都是递归法,若不用递归我们该怎么弄,不用递归,我们就得需要栈这个结构,代码整体不变,把最后递归的部分改成把key左右两个区间全入栈,先右区间入栈再左区间入栈,因为栈是后进先出原则,出栈就是左区间先出栈,直到栈空,入栈的条件左指针小于Key-1,右指针大于key+1。
画图看一下
区间边界值入栈,来替代了递归
代码如下
#include "stack.h"
int yici(int* a,int left,int right)
{int mid = sanshuquzhong(a, left, right);swap(&a[mid], &a[left]);int key = left;int begin = left;int end = right;while (begin < end){while (begin < end && a[end] >= a[key]){end--;}while (begin < end && a[begin] <= a[key]){begin++;}swap(&a[begin], &a[end]);}swap(&a[key], &a[begin]);key = begin;return key;
}
void QuickSortNonR(int* a, int left, int right)
{if (right - left + 1 < 10){charu(a + left, right - left + 1);}else{Stack as;StackInit(&as);StackPush(&as, right);StackPush(&as, left);while (!StackEmpty(&as)){int begin1 = StackTop(&as);StackPop(&as);int end1 = StackTop(&as);StackPop(&as);int key = yici(a, begin1, end1);if (key + 1 < end1){StackPush(&as, end1);StackPush(&as, key + 1);}if (key - 1 > begin1){StackPush(&as, key - 1);StackPush(&as, begin1);}}StackDestroy(&as);}
}
5.快速排序特性

快速排序的特性总结:1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫 快速 排序2. 时间复杂度: O(N*logN)3. 空间复杂度: O(logN)4. 稳定性:不稳定
结束语
快排有关知识就总结完了,我认为快速排序这个排序还是蛮重要的,大家要对这个排序更加重视,最后一个排序就是归并排序了,留在下篇博客说
0K,本篇博客结束!!!
相关文章:
【数据结构】排序——快速排序
前言 本篇博客我们继续介绍一种排序——快速排序,让我们看看快速排序是怎么实现的 💓 个人主页:小张同学zkf ⏩ 文章专栏:数据结构 若有问题 评论区见📝 🎉欢迎大家点赞👍收藏⭐文章 目录 …...
Matlab 怎么查找矩阵中所有0的数据并赋值
index find(X40); X4(index)57.71527;...
开发一个HTTP模块
开发一个HTTP模块 HTTP模块的数据结构ngx_module_t模块的数据结构ngx_http_module_t数据结构ngx_command_s 数据结构 定义一个HTTP模块处理用户请求返回值获取URI和参数方法名URIURL协议版本 获取HTTP头获取HTTP包体 发送响应发送HTTP头发送内存中的字符串作为包体返回一个Hell…...
vue2实现复制,粘贴功能,使用vue-clipboard2插件
一、需求说明 在项目中 点击按钮 复制 某行文本是很常见的 应用场景, 在 Vue 项目中实现 复制功能 需要借助 vue-clipboard2 插件。 二、代码实现 1、安装 vue-clipboard2 依赖 ( 出现错误的话,可以试试切换成淘宝镜像源 npm config set r…...
【软件测试】 1+X初级 功能测试试题
【软件测试】 1X初级 功能测试试题 普通员工登录系统,在“个人信息维护”模块,可以查看和维护个人信息。个人信息维护需求包括用户(UI)页面、业务规则两部分。 UI 界面 个人信息维护 修改基本信息 业务规则 1. 个人信息维护页面…...
zynq启动和程序固化流程
普通FPGA启动 FPGA的启动方式主要包含主动模式、被动模式和JTAG模式。 主动模式(AS模式) 当FPGA器件上电时,它作为控制器从配置器件EPCS中主动发出读取数据信号,并将EPCS的数据读入到自身中,实现对FPGA的编程。这种…...
CSS3实现彩色变形爱心动画【附源码】
随着前端技术的发展,CSS3 为我们提供了丰富的动画效果,使得网页设计更加生动和有趣。今天,我们将探讨如何使用 CSS3 实现一个彩色变形爱心加载动画特效。这种动画不仅美观,而且可以应用于各种网页元素,比如加载指示器或…...
【JVM基础篇】Java的四种垃圾回收算法介绍
文章目录 垃圾回收算法垃圾回收算法的历史和分类垃圾回收算法的评价标准标记清除算法优缺点 复制算法优缺点 标记整理算法(标记压缩算法)优缺点 分代垃圾回收算法(常用)JVM参数设置使用Arthas查看内存分区垃圾回收执行流程分代GC算…...
Kodcloud可道云安装与一键发布上线实现远程访问详细教程
文章目录 1.前言2. Kodcloud网站搭建2.1. Kodcloud下载和安装2.2 Kodcloud网页测试 3. cpolar内网穿透的安装和注册4. 本地网页发布4.1 Cpolar云端设置4.2 Cpolar本地设置 5. 公网访问测试6.结语 1.前言 本文主要为大家介绍一款国人自研的在线Web文件管理器可道云,…...
python杨辉三角的两种书写方式
第一种(设置二维列表设置每个元素为0进行替换元素) 代码演示: n eval(input("请输入想要的行数")) lst[[0 for j in range(n)] for i in range(n)] # lst2[[0]*n]*n for i in range(n):for j in range(i1):if j0 or ji:lst[i][j…...
【CSS in Depth 2精译】2.5 无单位的数值与行高
当前内容所在位置 第一章 层叠、优先级与继承第二章 相对单位 2.1 相对单位的威力2.2 em 与 rem2.3 告别像素思维2.4 视口的相对单位2.5 无单位的数值与行高 ✔️2.6 自定义属性2.7 本章小结 2.5 无单位的数值与行高 有些属性允许使用无单位的数值(unitless value…...
【人脸识别、Python实现】PyQt5人脸识别管理系统
PyQt5人脸识别管理系统 项目描述主要功能效果展示获取源码 项目描述 接的一个基于宿舍管理系统与人脸识别的小单子。然后我把它优化了一些,现在开源一下。有需要的小伙伴自取,点个免费的关注就行 主要功能 1、录入学生基本信息、录入人脸 2、主页面展…...
软设之观察者模式
设计模式中,观察者模式的意图是:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 比如说,有一个新闻网站,订阅的用户众多,假如说管理员发布了一…...
deep learning 环境配置
1 NVIDIA驱动安装 ref link: https://blog.csdn.net/weixin_37926734/article/details/123033286 2 cuda安装 ref link: https://blog.csdn.net/qq_63379469/article/details/123319269 进去网站 https://developer.nvidia.com/cuda-toolkit-archive 选择想要安装的cuda版…...
09磁盘管理
一、磁盘管理 1.磁盘基础知识 (1)磁盘接口类型 个人电脑, 硬盘接口分为IDE类型和SATA类型 服务器版分为SCSI类型和SAS类型 (2)磁盘命名方式 windows中硬盘命名方式是c,d,e盘 linux中硬盘命名方式为 系统…...
Node.js Stream
Node.js Stream Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它允许开发者使用 JavaScript 编写服务器端代码。Node.js 的一个核心特性是其对流(Stream)的处理能力。流是一种在 Node.js 中处理读/写文件、网络通信或任何端到端…...
简化嵌入式Linux开发:在Ubuntu上安装和配置交叉编译环境的高效方法
在嵌入式Linux开发中,我们通常需要在Ubuntu上安装交叉编译工具链,并配置相关文件。编译过程中,如果遇到依赖库问题,还需要手动查找并编译开源源码。这些步骤较为繁琐,为了简化操作,我们可以尝试以下方案&am…...
Photoshop批量处理图片分辨率
整理一些文件的时候,发现需要处理大量图片的尺寸和分辨率。如果一张一张的处理就会很慢,搜了下,Photoshop提供自动批量处理的方法。在此记录一下。 一、说说批量处理图片 1.打开PS软件并导入图片,我用的是比较老的版本cs4&#…...
TCP协议的三次握手和四次挥手(面试)
三次握手 首先可以简单的回答: 1、第一次握手:客户端给服务器发送一个 SYN 报文。 2、第二次握手:服务器收到 SYN 报文之后,会应答一个 SYNACK 报文。 3、第三次握手:客户端收到 SYNACK 报文之后…...
css看见彩虹,吃定彩虹
css彩虹 .f111 {width: 200px;height: 200px;border-radius: 50%;box-shadow: 0 0 0 5px inset red, 0 0 0 10px inset orange, 0 0 0 15px inset yellow, 0 0 0 20px inset lime, 0 0 0 25px inset aqua, 0 0 0 30px inset blue, 0 0 0 35px inset magenta;clip-path: polygo…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
c# 局部函数 定义、功能与示例
C# 局部函数:定义、功能与示例 1. 定义与功能 局部函数(Local Function)是嵌套在另一个方法内部的私有方法,仅在包含它的方法内可见。 • 作用:封装仅用于当前方法的逻辑,避免污染类作用域,提升…...
如何配置一个sql server使得其它用户可以通过excel odbc获取数据
要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据,你需要完成以下配置步骤: ✅ 一、在 SQL Server 端配置(服务器设置) 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到:SQL Server 网络配…...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...
AD学习(3)
1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分: (1)PCB焊盘:表层的铜 ,top层的铜 (2)管脚序号:用来关联原理图中的管脚的序号,原理图的序号需要和PCB封装一一…...
高端性能封装正在突破性能壁垒,其芯片集成技术助力人工智能革命。
2024 年,高端封装市场规模为 80 亿美元,预计到 2030 年将超过 280 亿美元,2024-2030 年复合年增长率为 23%。 细分到各个终端市场,最大的高端性能封装市场是“电信和基础设施”,2024 年该市场创造了超过 67% 的收入。…...
Python的__call__ 方法
在 Python 中,__call__ 是一个特殊的魔术方法(magic method),它允许一个类的实例像函数一样被调用。当你在一个对象后面加上 () 并执行时(例如 obj()),Python 会自动调用该对象的 __call__ 方法…...



