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


在这里插入图片描述

🎬 鸽芷咕:个人主页

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

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

📋 前言

快速排序这个名词,快排之所以叫快排肯定是有点东西的。他在处理大规模数据集时表现及其出色。其思想是在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.mzph.cn/news/580994.shtml

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

相关文章

怎么设置Facebook双重验证,让Facebook账号更稳定?

对于跨境电商而言&#xff0c;Facebook 账号被封是常有的事&#xff0c;原因之一可能是没有给 Facebook 设置双重验证&#xff0c;今天就和大家科普一下什么是 Facebook双重验证以及如何设置&#xff0c;另外&#xff0c;作为跨境卖家&#xff0c;我们还需要了解如何保护 Faceb…

Bert-vits2最终版Bert-vits2-2.3云端训练和推理(Colab免费GPU算力平台)

对于深度学习初学者来说&#xff0c;JupyterNoteBook的脚本运行形式显然更加友好&#xff0c;依托Python语言的跨平台特性&#xff0c;JupyterNoteBook既可以在本地线下环境运行&#xff0c;也可以在线上服务器上运行。GoogleColab作为免费GPU算力平台的执牛耳者&#xff0c;更…

视频格式网络地址转换视频到本地,获取封面、时长,其他格式转换成mp4

使用ffmpeg软件转换网络视频&#xff0c;先从官网下载对应操作系统环境的包 注意:网络地址需要是视频格式结尾&#xff0c;例如.mp4,.flv 等 官网地址&#xff1a;Download FFmpeg window包&#xff1a; linux包&#xff1a; 如果下载缓慢&#xff0c;下载迅雷安装使用…

Pycharm2023版本:Python远程调试配置详解

工欲善其事&#xff0c;必先利其器 首先你需要选择一个专业版本的pycharm&#xff0c;社区版本不支持远程配置功能&#xff0c;专业版下载地址&#xff1a;Pycharm 2023 双击程序进行安装&#xff0c;30天内免费试用&#xff0c;如果想要永久使用&#xff0c;办法你懂的&…

GBASE南大通用 GCDW阿里云计算巢:自动化部署云原生数据仓库

目前&#xff0c;GBASE南大通用已与阿里云计算巢合作&#xff0c;双方融合各自技术优势&#xff0c;助力企业用户实现云上数据仓库的自动化部署&#xff0c;让用户在云端获取数据仓库服务“更简单”&#xff0c;让用户在云端使用数据仓库服务“更便捷”&#xff0c;满足企业用户…

uniapp 安卓模拟器链接

下载genymotion 安装 配置adb路径 模拟端口设为 5307

数据库系统课程设计(高校成绩管理数据库系统的设计与实现)

目录 1、需求分析 1 1.1 数据需求描述 1 1.2 系统功能需求 3 1.3 其他性能需求 4 2、概念结构设计 4 2.1 局部E-R图 4 2.2 全局E-R图 5 2.3 优化E-R图 6 3、逻辑结构设计 6 3.1 关系模式设计 6 3.2 数据类型定义 6 3.3 关系模式的优化 8 4、物理结构设计 9 4.1 聚…

CSDN规则详解——csdn那些你不知道的事儿(点赞、评论、收藏)

文章目录 每日一句正能量前言点赞评论收藏原力等级和博客等级后记 每日一句正能量 “只有奋斗者才能成为胜利者&#xff0c;只有坚持者才能创造奇迹。” - 迈克尔乔丹 这句话来自于世界著名篮球运动员迈克尔乔丹&#xff0c;他以无与伦比的天赋和努力成为了篮球界的传奇人物。他…

基于ChatGpt,Java,SpringBoot,Vue,Milvus向量数据库的定制化聊天Web demo

customized chat GitHub - bigcyy/customized-chatgpt: 基于ChatGpt&#xff0c;Java&#xff0c;SpringBoot&#xff0c;Vue&#xff0c;Milvus向量数据库的定制化聊天Web demo 简介 基于ChatGpt&#xff0c;Java&#xff0c;SpringBoot&#xff0c;Vue&#xff0c;Milvus向…

模式识别与机器学习-SVM(线性支持向量机)

线性支持向量机 线性支持向量机间隔距离学习的对偶算法算法:线性可分支持向量机学习算法线性可分支持向量机例子 谨以此博客作为复习期间的记录 线性支持向量机 在以上四条线中&#xff0c;都可以作为分割平面&#xff0c;误差率也都为0。但是那个分割平面效果更好呢&#xff1…

Java文件操作实现doc格式转pdf

使用场景 在进行生成文档或者报告的时候&#xff0c;生成的word想要转换为pdf格式&#xff0c;这样才能保证报告的不可修改性&#xff0c;但是aspose-words的工具的License令人头疼&#xff0c;这篇文章就是解决这个doc转pdf的License的问题。话不多说&#xff0c;直接上实操。…

箭头函数的泛型,typescript中怎么写

TypeScript——泛型&#xff08;函数泛型、模板类型可以是多个、泛型的错误、泛型函数变量、泛型函数类型接口、泛型类1、泛型类2、泛型约束、泛型参数的默认类型&#xff09;、声明文件、Vue3.0集成ts_typescript 泛型函数-CSDN博客

【C语言】自定义类型:结构体深入解析(三)结构体实现位段最终篇

文章目录 &#x1f4dd;前言&#x1f320;什么是位段&#xff1f;&#x1f309; 位段的内存分配&#x1f309;VS怎么开辟位段空间呢&#xff1f;&#x1f309;位段的跨平台问题&#x1f320; 位段的应⽤&#x1f320;位段使⽤的注意事项&#x1f6a9;总结 &#x1f4dd;前言 本…

月薪高达6W,多家大厂急招鸿蒙开发工程师,现在转还来得及吗?

近期&#xff0c;“安卓版本与鸿蒙不再兼容”的词条登上微博热搜&#xff0c;华为鸿蒙加速按下向“纯血鸿蒙”蜕变的启动键&#xff0c;欲与 iOS、安卓在市场三分天下。 一批嗅觉灵敏的互联网大厂&#xff0c;已经完成或开始启动开发鸿蒙原生 APP&#xff0c;也于近期发布了和…

Java之ThreadLocal 详解

ThreadLocal 详解 原文地址&#xff1a;https://juejin.cn/post/6844904151567040519open in new window。 什么是ThreadLocal&#xff1f; ThreadLocal提供线程局部变量。这些变量与正常的变量不同&#xff0c;因为每一个线程在访问ThreadLocal实例的时候&#xff08;通过其…

视频流媒体直播云服务管理平台EasyNVS长时间运行出现崩溃情况是什么原因?该如何解决?

EasyNVS云管理平台具备汇聚与管理EasyGBS、EasyNVR等平台的能力&#xff0c;可以将接入的视频资源实现统一的视频能力输出&#xff0c;支持远程可视化运维等管理功能&#xff0c;还能解决设备现场没有固定公网IP却需要在公网直播的需求。 有用户反馈&#xff0c;在长时间不间断…

CGAL的D维范围树和线段树

范围树和线段树是两种数据结构&#xff0c;用于高效地处理和查询数据。 范围树&#xff08;Range Tree&#xff09;是一种二叉树&#xff0c;它通过递归地将每个节点分割成两个子节点来存储一个点集。每个节点表示一个范围&#xff0c;并且存储该范围内所有点的最小和最大值。范…

静物摄影在UE5里运用几点记要

被摄体&#xff0c;相机与光源的关系&#xff0c;要增强立体感&#xff0c;摄像机与光源的位置关系要错开&#xff1b;b的立体感要更强 漫反射与点光源&#xff0c;UE5太阳光属于漫反射&#xff0c;整体比较柔和&#xff0c;但是阴影处比较黑&#xff1b;摄影棚会用反光板来增亮…

【模型】模型量化技术:动态范围、全整数和Float16量化

目录 一 动态范围量化 二 全整数量化 三 float16量化 通常&#xff0c;表示神经网络的数据类型是32位浮点数&#xff08;float32&#xff09;&#xff0c;这种数据类型可以提供高精度的计算&#xff0c;但是在计算资源和存储空间有限的设备上运行神经网络时&#xff0c;会带…

SpringBoot 异步编程浅谈

1. 需求背景 当我们需要提高系统的并发性能时&#xff0c;我们可以将耗时的操作异步执行&#xff0c;从而避免线程阻塞&#xff0c;提高系统的并发性能。例如&#xff0c;在处理大量的并发请求时&#xff0c;如果每个请求都是同步阻塞的方式处 理&#xff0c;系统的响应时间会…