快速排序:高效分割与递归,排序领域的王者算法


在这里插入图片描述

🎬 鸽芷咕:个人主页

 🔥 个人专栏: 《数据结构&算法》《粉丝福利》

⛺️生活的理想,就是为了理想的生活!

📋 前言

快速排序这个名词,快排之所以叫快排肯定是有点东西的。他在处理大规模数据集时表现及其出色。其思想是在Hoare于1962年提出的一种二叉树结构的交换排序方法,利用了二叉树的思想来构建排序。

文章目录

  • 📋 前言
  • 一、快速排序的介绍
  • 二、快速排序的实现
    • 2.1 hoare版本
    • 为什么每次相遇位置都比key要小
    • 2.2 挖坑法
    • 2.3 前后指针版本
  • 三、快速排序的优化
      • 快排的最坏情况
    • 3.1 三数取中
    • 3.2 递归到小的子区间时使用插入排序
    • 3.3 快速排序的最终代码
  • 四、快速排序的总结
          • 快速排序的特性总结:

一、快速排序的介绍

快速排序是一种基于分治思想的高效排序算法,由Tony Hoare于1960年提出。它的核心思想是通过选择一个基准元素,将数组划分成两个子数组,使得左边的子数组元素都小于基准,右边的子数组元素都大于基准,然后对这两个子数组分别进行递归排序。

二、快速排序的实现

快速排序是一种基于分治思想的高效排序算法其核心就是每次找到最中间的位置然后再进行递归继续找到最中间的位置然后再分割一直分割到只剩一个数的时候那么这个数组就是有序了。

  • 🔥 注:这里红色代表中间值。
  • 每次找到中间值之后利用分治思想,大问题化为小问题
  • 然后递归进行排序当递归完成时每个中间值都找到就是排序好的时候

在这里插入图片描述

而要搞定一个排序首先最先解决的就是其中单趟排序下面就是各位大佬想出来的单趟排序方法:

  • 先把部分的单趟排序搞出来后面来实现整体排序就简单多了

2.1 hoare版本

在这里插入图片描述

hoare版本的方法就是那 key 选择作为中间值,然后用left 当做最左边的下标 right 当做最右边的下标

  • 每次right 先走去找比 key 值要小的数,找到了就停下来
  • 然后left 在走去找比 key 值要大的数,找到了之后 left 和 right 的值进行交换

一直重复下去直到他俩相遇的时候就是找到中间值的位置了了,然后把 key 这个中间值和 left 或者 right 交换到中间值的位置

🍸 代码演示:

//hoare法排序
int PartSort1(int* a, int left, int right)
{int keyi = left;while (left < right){while (left < right && a[right] >= a[keyi]){right--;}while (left < right && a[left] <= a[keyi]){left++;}Swap(&a[right], &a[left]);}Swap(&a[keyi], &a[left]);return left;
}

为什么每次相遇位置都比key要小

  1. 因为每次都是 right 先走所以是 R 先找到比可key 小的

这时就有俩总情况了分别是 R 不动 left 走
他俩相遇

  • 由于R已经找到了比key要小了,所以当他们相遇和 key 交换时 相遇位置都比key要小

在这里插入图片描述

或者 R 和 L 交换完成之后 L 就是最小的了,这时是 L 不动 R 先走

在这里插入图片描述

2.2 挖坑法

在这里插入图片描述

有人呢觉得hoare 大佬的方法单趟排序太麻烦了每次控制好左右,所以就提出改进了 hoare 的排序方法命名为挖坑法

简单点来讲 key 用来记录第一个值,然后把它的下标记下来 当做第一个坑位

  • 然后 right 向前找找到比 key 的值小的时候就和 left 交换 并且把 hole 坑位给记录为新的位置
  • 然后 left 向前走,找到比 key 值要大的时候 和 right 交换 并记录下新的坑位 hole

🍸 代码演示:

//快速排序挖坑法
int PartSort2(int* a, int begin, int end)
{//三数取中int midi = GetMidi(a, begin, end);Swap(&a[begin], &a[midi]);//保存第一个坑位的值int key = a[begin];//每个坑位的下标用于交换数据int hole = begin;while (begin < end){while (begin < end && a[end] >= a[hole]){end--;}Swap(&a[end], &a[hole]);hole = end;while (begin < end && a[begin] <= a[hole]){begin++;}Swap(&a[begin], &a[hole]);hole = begin;}a[hole] = key;return begin;}

2.3 前后指针版本

在这里插入图片描述

时至今日诶有些人还是觉得前面俩种方法太麻烦了,为什么一定要右边先走所以就有了第三种方法前后指针法大大优化了快排的方法。

这里的核心思想是还是一样,key是我们需要进行比较的中间值

  • prve 指针初始化的位置为 begin 的位置
  • cur 指针初始化为 prve + 1 的位置

然后每次 cur的值和 key 的值做比较 如果小于 key 那么 prve 就+1 , cur 和 prve 交换位置

  • cur 继续向前走一步 如果 cur 的值比 key大的话就继续++ , prve 不动这样一直循环下去

注:当循环结束的时候也就是 cur > end 数组长度的时候,就吧 cur 和 key 交换值,这样中间值就到了我们预想的位置

//前后指针法
int PartSort3(int* a, int begin, int end)
{int prve = begin;int cur = prve + 1;int keyi = begin;while (cur <= end){if (a[cur] < a[keyi] && prve++ != cur){Swap(&a[prve], &a[cur]);}cur++;}if (cur > end){Swap(&a[keyi], &a[prve]);}return prve;
}

三、快速排序的优化

快排的最坏情况

快速排序虽然很快时间复杂度一眼看上去都是 N*longN 但这只是最好的情况:
在这里插入图片描述
🔥 注:最坏的情况复杂度是 N*N,每次 key 选的值都是最小的

在这里插入图片描述
每次进行递归并不是完全二叉树的样子,每次都需要全部遍历一遍才找到一个数据

  • 所以就有了三数取中这个算法

3.1 三数取中

顾名思义,三数取中就是把,left 和 mid right 里面找到一个中间数的下标来进行返回

  • 然后再把 leftmid 来进行交换这个每次 key 取值都是靠近中间数

这样就完美解决了二叉树的最差情况使得效率大大提升

🍸 代码演示:


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

3.2 递归到小的子区间时使用插入排序

快排的最坏情况我们优化了,但是其实还有一个优化方法,现在我们知道了快排是利用二叉树的思想但是二叉树的递归的弊端就是栈的太大了:

  • 而二叉树的叶子节点就占了整课树的 %50 假如我们在 递归区间只有10的时候就使用插入排序呢?

因为如果有很多的数据进行排序的话 快排的特性是每次到找到中间值然后再递归所以但递归到了10这个区间的时候就大致有序了,而插入排序对有序数组排序最快是 O(N)

  • 所以我们选择当区间为 10 的时候采用插入排序来进行排序

🔥 那么这样的递归栈消耗可以减少多少呢?
在这里插入图片描述
这里就可以看到递归的栈区消耗优化达到了惊人 %80

🍸 代码演示:

//快速排序
void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;if ((end - begin) > 10){int div = PartSort3(a, begin, end);QuickSort(a, begin, div - 1);QuickSort(a, div + 1, end);}else{InsertSort(a+begin, end-begin+1);}
}

3.3 快速排序的最终代码

好了所以关于快排的方法我们讲完了,下面就来看看最终的快排代码吧!

//快速排序
void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;if ((end - begin) > 10){//三数取中int midi = GetMidi(a, begin, end);Swap(&a[begin], &a[midi]);int prve = begin;int cur = prve + 1;int keyi = begin;while (cur <= end){if (a[cur] < a[keyi] && prve++ != cur){Swap(&a[prve], &a[cur]);}cur++;}if (cur > end){Swap(&a[keyi], &a[prve]);}QuickSort(a, begin, prve - 1);QuickSort(a, prve + 1, end);}else{InsertSort(a+begin, end-begin+1);}
}

四、快速排序的总结

快速排序的特性总结:
  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

  2. 时间复杂度:O(N*logN)

在这里插入图片描述

  1. 空间复杂度:O(logN)

  2. 稳定性:不稳定

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

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

相关文章

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之线性布局容器Row组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之线性布局容器Row组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Row组件 沿水平方向布局容器。 子组件 可以包含子组件。 接口 Row(…

【头歌实训】Spark 完全分布式的安装和部署(新)

文章目录 第1关&#xff1a; Standalone 分布式集群搭建任务描述相关知识课程视频Spark分布式安装模式主机映射免密登录准备Spark安装包配置环境变量修改 spark-env.sh 配置文件修改 slaves 文件分发安装包启动spark验证安装 编程要求测试说明答案代码 第1关&#xff1a; Stand…

『精』CSS 小技巧之BEM规范

『精』CSS 小技巧之BEM规范 文章目录 『精』CSS 小技巧之BEM规范一、什么是BEM&#xff1f;二、BEM要怎么用&#xff1f;三、不用BEM会少个胳膊吗&#xff1f;&#x1f48a;四、Sass与BEM的结合&#x1f388;五、块与修饰符应放在一块&#x1f47f;参考资料&#x1f498;推荐博…

XIAO ESP32S3之物体检测加入视频流

一、前言 由于XIAO ESP32S3开发套件没有显示屏配件&#xff0c;因此加入http视频流功能&#xff0c;可通过浏览器请求ESP32S3上的视频流。 二、思路 1、XIAO ESP32S3启动后通过wifi连接到AP&#xff1b; 2、启动http服务器&#xff0c;注册get_mjpeg处理函数&#xff1b; 3…

PyTorch实战:基于Seq2seq模型处理机器翻译任务(模型预测)

文章目录 引言数据预处理加载字典对象en2id和zh2id文本分词 加载训练好的Seq2Seq模型模型预测完整代码结束语 引言 随着全球化的深入&#xff0c;翻译需求日益增长。传统的人工翻译方式虽然质量高&#xff0c;但效率低&#xff0c;成本高。机器翻译的出现&#xff0c;为解决这…

虚函数的讲解

文章目录 虚函数的声明与定义代码演示基类Person派生类Man派生类Woman 测试代码动态绑定静态绑定访问私有虚函数总结一下通过成员函数指针调用函数的方式 虚函数的声明与定义 虚函数存在于C的类、结构体等中&#xff0c;不能存在于全局函数中&#xff0c;只能作为成员函数存在…

IntelliJ IDEA [插件 MybatisX] mapper和xml间跳转

文章目录 1. 安装插件2. 如何使用3. 主要功能总结 MybatisX 是一款为 IntelliJ IDEA 提供支持的 MyBatis 开发插件 它通过提供丰富的功能集&#xff0c;大大简化了 MyBatis XML 文件的编写、映射关系的可视化查看以及 SQL 语句的调试等操作。本文将介绍如何安装、配置和使用 In…

知识库问答LangChain+LLM的二次开发:商用时的典型问题及其改进方案

前言 如之前的文章所述&#xff0c;我司下半年成立大模型项目团队之后&#xff0c;我虽兼管整个项目团队&#xff0c;但为让项目的推进效率更高&#xff0c;故分成了三大项目组 第一项目组由霍哥带头负责类似AIGC模特生成系统第二项目组由阿荀带头负责论文审稿GPT以及AI agen…

基于飞浆OCR的文本框box及坐标中心点检测JSON格式保存文本

OCR的文本框box及JSON数据保存 需求说明 一、借助飞浆框出OCR识别的文本框 二、以圆圈形式标出每个框的中心点位置 三、以JSON及文本格式保存OCR识别的文本 四、以文本格式保存必要的文本信息 解决方法 一、文本的坐标来自飞浆的COR识别 二、借助paddleocr的draw_ocr画出…

go语言,ent库与gorm库,插入一条null值的time数据

情景介绍 使用go语言&#xff0c;我需要保存xxxTime的字段至数据库中&#xff0c;这个字段可能为空&#xff0c;也可能是一段时间。我采取的是统一先赋值为空&#xff0c;若有需要&#xff0c;则再进行插入&#xff08;需要根据另一个字段判断是否插入&#xff09; 在我的数据…

最新国内使用GPT4教程,GPT语音对话使用,Midjourney绘画,ChatFile文档对话总结+DALL-E3文生图

一、前言 ChatGPT3.5、GPT4.0、GPT语音对话、Midjourney绘画&#xff0c;文档对话总结DALL-E3文生图&#xff0c;相信对大家应该不感到陌生吧&#xff1f;简单来说&#xff0c;GPT-4技术比之前的GPT-3.5相对来说更加智能&#xff0c;会根据用户的要求生成多种内容甚至也可以和…

HPCC:高精度拥塞控制

HPCC&#xff1a;高精度拥塞控制 文章目录 HPCC&#xff1a;高精度拥塞控制摘要1 引言1.1 背景1.2 现有CC的局限性1.3 HPCC的提出 2 研究动机2.1 大型RDMA部署2.2 RDMA目标2.3 当前RDMA CC中的权衡DCQCNTIMELY 2.4 下一代高速CC 3 技术方案3.1 INT3.2 HPCC设计3.3 HPPC的参数 4…

浅谈WPF之ToolTip工具提示

在日常应用中&#xff0c;当鼠标放置在某些控件上时&#xff0c;都会有相应的信息提示&#xff0c;从软件易用性上来说&#xff0c;这是一个非常友好的功能设计。那在WPF中&#xff0c;如何进行控件信息提示呢&#xff1f;这就是本文需要介绍的ToolTip【工具提示】内容&#xf…

数据结构入门到入土——List的介绍

目录 一&#xff0c;什么是List&#xff1f; 二&#xff0c;常见接口介绍 三&#xff0c;List的使用 一&#xff0c;什么是List&#xff1f; 在集合框架中&#xff0c;List是一个接口&#xff0c;继承自Collection。 Collection也是一个接口&#xff0c;该接口中规范了后序容…

MATLAB中./和/,.*和*,.^和^的区别

MATLAB中./和/&#xff0c;.*和*&#xff0c;.^ 和^ 的区别 MATLAB中./和/&#xff0c;.*和*&#xff0c;.^ 和^ 的区别./ 和 / 的区别.//实验实验结果 .* 和 * 的区别.**实验实验结果 .^ 和^ 的区别.^n^n实验运行结果 MATLAB中./和/&#xff0c;.和&#xff0c;.^ 和^ 的区别 …

关于SQL时间盲注(基于sleep函数)的手动测试、burpsuite爆破、sqlmap全自动化注入

SQL时间注入是一种常见的SQL注入攻击方式&#xff0c;攻击者通过在SQL语句中注入时间相关的代码&#xff0c;来获取敏感信息或者执行非法操作。其基本原理如下&#xff1a; 攻击者向Web应用程序中输入一段恶意代码&#xff0c;通过SQL语句查询数据库&#xff0c;并注入时间相关…

钉钉机器人接入定时器(钉钉API+XXL-JOB)

钉钉机器人接入定时器&#xff08;钉钉APIXXL-JOB&#xff09; 首先需要创建钉钉内部群 在群设置中找到机器人选项 选择“自定义”机器人 通过Webhook接入自定义服务 创建完成后会生成一个send URL和一个加签码 下面就是干货 代码部分了 DingDingUtil.sendMessageByText(webho…

什么是迁移学习(Transfer Learning)?定义,优势,方法

迄今为止&#xff0c;大多数人工智能&#xff08;AI&#xff09;项目都是通过监督学习技术构建的。监督学习是一种从无到有构建机器学习&#xff08;ML&#xff09;模型的方法&#xff0c;它对推动AI发展起到了关键作用。然而&#xff0c;由于需要大量的数据集和强大的计算能力…

账号租号平台PHP源码,支持单独租用或合租使用

源码简介 租号平台源码&#xff0c;采用常见的租号模式。 平台的主要功能如下&#xff1a; 支持单独租用或采用合租模式&#xff1b; 采用易支付通用接口进行支付&#xff1b; 添加邀请返利功能&#xff0c;以便站长更好地推广&#xff1b; 提供用户提现功能&#xff1b;…

PHP的Laravel加一个小页面出现问题(whereRaw的用法)

1.权限更新问题 因为是已经有样例了所以html和php页面很快写出来了 然后就是页面写完了路由不知道在哪写&#xff0c;后来想起来之前有要开权限来着&#xff0c;试了一下&#xff0c;还是不行&#xff0c;不过方向是对了 这是加的路由&#xff0c;不过需要在更新一下权限 这…