最新动态
【数据结构】八大排序之快速排序算法
2024-12-21 18:53

🦄个人主页:修修修也

🎏所属专栏:数据结构

⚙️操作环境:Visual Studio 2022


目录

一.快速排序简介及思路

二.快速排序代码实现的三种方式

【数据结构】八大排序之快速排序算法

📌左右交换法

📌挖坑填坑法

📌前后指针法

三.快速排序的时间复杂度分析

四.快速排序的优化

🎏优化选key方式

📌随机选key法

📌三数取中法

🎏小区间优化

📌小区间优化的原理

📌小区间优化的代码实现

五.借助栈实现非递归快速排序

📌为什么要将递归的快速排序算法改为非递归?

📌递归函数改非递归的思路

📌快速排序改非递归的思路

📌快速排序改非递归的代码实现

六.快速排序的三路划分

结语


快速排序(Quick Sort)是一种效率较高的交换排序算法.

它的基本思想是:

  • 通过一趟排序将待排数据分割成独立的两部分
  • 其中一部分数据的关键字均比另一部分数据的关键字小
  • 可分别对这两部分数据继续进行排序,以达到整个序列有序的目的.

算法动图演示:


我们了解了快速排序的基本思想通过一趟排序将待排数据分割成独立的两部分之后,在代码的实现上,其实就有很多可以自由发挥的空间,如下较为主流的快速排序有三种实现思路,接下来我将一一带大家理解这三个思路并使用它们实现快排算法:

注:本文的快排实现思路均以升序为例!

左右交换法的思路是:

  1. 先选定当前待排序列的首元素位置的值为基准值(key).
  2. 然后设置一个右指针,使其从后向前遍历,找到比基准值(key)小的元素就停下来.
  3. 再设置一个左指针,使其从前向后遍历,找到比基准值(key)大的元素就停下来.
  4. 当左右指针都找到相应元素时,交换它们找到的元素.
  5. 重复步骤2~4,直到左右指针相遇
  6. 左右指针相遇后,将基准值(key)与相遇位置做交换,此时数组已经被重新一分为二成两个新的待排子序列.
  7. 分别继续对新的待排子序列继续执行步骤1~6排序,直到所有元素都排列在相应位置上为止.

 清楚了左右交换法实现快排的思路后,我们编写实现代码就比较容易了,代码如下:

 

挖坑填坑法是基于快排的基础思想提出的一种创新实现的思路,它的思路是这样的:

  1. 记录下当前待排序列的首元素为基准值(key).
  2. 此时认为首元素的位置空缺的,即该位置成为了一个坑.
  3. 设置一个右指针,使其从后向前遍历,找到比基准值(key)小的元素停下来将其填入刚才的坑位中,此时认为右指针找到的这个元素位置又形成了一个坑.
  4. 设置一个左指针,使其从前向后遍历,找到比基准值(key)大的元素停下来将其填入刚才的坑位中,此时认为左指针找到的这个元素位置又形成了一个坑.
  5. 左右指针不断向中间挪动不断填坑又形成新坑,直到两指针相遇
  6. 最后将基准值(key)填入左右指针相遇位置的坑中,此时数组已经被重新一分为二成两个新的待排子序列.
  7. 分别继续对新的待排子序列继续执行步骤1~6排序,直到所有元素都排列在相应位置上为止.

挖坑填坑法实现快排代码如下:

 

前后指针法思路为:

  1. 先选定当前待排序列的首元素位置的值为基准值(key).
  2. 设立前指针prev,使其指向序列开头,即基准值位置
  3. 设立后指针cur,使其指向prev指针的后一个位置
  4. 判断cur指向的数据是否小于key:如果小于,则prev后移一位,然后将cur指向的内容与prev指向的内容交换, 然后cur指针++;如果不小于,则cur++.
  5. 循环步骤4,直到cur移动到超出序列范围时,交换prev位置和基准位置的值,此时数组已经被重新一分为二成两个新的待排子序列.
  6. 分别继续对新的待排子序列继续执行步骤1~5排序,直到所有元素都排列在相应位置上为止.

前后指针法算法演示:

前后指针法实现快排代码如下:

 

"快速排序的平均时间为 ,其中n为待排序序列中数据的个数,k为某个常数,经验证明,在所有同数量级的此类(先进的)排序算法中,快速排序的常数因子k最小.因此,就平均时间而言,快速排序是目前被认为最好的一种内部排序方法.

通常,快速排序被认为是,在所有同数量级(O(nlogn))的排序算法中,其平均性能最好.但是,若初始数据序列按关键字有序或基本有序时,快速排序将蜕化为冒泡排序,其时间复杂度为O(n^2)."

                                ——《数据结构》严蔚敏

也就是说,快排的时间复杂度我们可以认为是O(nlogn),但当遇到原数组本身有序的情况下,其时间复杂度就会退化至O(n^2),这个其实很好理解,举个例子就明白了:

该树每层需要遍历一遍数组,时间复杂度为n,而树高为,因此最优状态下快排的时间复杂度仅为O(nlogn).

最坏情况下,即每趟选择key时都恰好选择到数组最大或最小的值时(即每一层都只能确定一个数字位置),快排的时间复杂度如下单支树:

树每层遍历一遍数组,时间复杂度为n,而树高也为n,因此最坏状态下快排的时间复杂度为O(n^2).

综上,对快排时间复杂度的分析,我们不光理解了为什么快排排先天有序的数组时反而效率最差,同样也为我们后续对快排算法的优化提供了思路.


既然在快排在面对原本就接近有序的数组时排序会因为key值的选取导致效率降低,那么我们不妨优化一下我们快排时选key的方式,下面为大家介绍两种常用的优化选key的方式:

📌随机选key法

随机选key的思路为:

  1. 先使用rand()函数随机选出一个在[left,right]范围内的下标值randi
  2. 将randi下标的数据和keyi下标的数据互换

随机选key函数的实现:

 

结合随机选key法实现快排

我们写好随机选key函数后只需要在正常快排函数中选定keyi后(如下函数的第15行后)调用一下随机选keyi函数就可以将随机选出的key值和原本的key值做交换了.

实现代码如下:

 

📌三数取中法

三数取中法的思路是:

  1. 比较序列首元素,尾元素,中间元素,取三者中的中间值作为midi
  2. 将midi下标的数据和keyi下标的数据互换

三数取中函数的实现:

 

结合三数取中法实现快排

我们写好三数取中函数后只需要在正常快排函数中选定keyi后(如下函数的第45行后)调用一下三数取中函数就可以将三数取中选出的key值和原本的key值做交换了.

实现代码如下:

 

📌小区间优化的原理

快排的递归展开思路类似于二叉树,因此它们拥有同样的弊病,就是越靠近树的底部,空递归的情况就越多,并且空递归的规模量非常大,拿下面这颗树来举例:

我们递归遍历该树,发现空递归(紫色)访问次数竟然和总有效访问次数(绿色)是相同.而对于快排来说,这样的空递归不仅浪费时间,而且是没有任何实际意义的.

因此我们可以考虑采用一种办法,将快排的递归范围加以限制,比如当我们不断分割快排子区间,当子区间数组元素小于10个数时,我们就不再进行快排递归排序,而使用直接插入排序来对该小区间进行排序,这样就可以有效的消灭超过一半的递归,从而提升快排的效率.


📌小区间优化的代码实现

清楚了上面的原理之后,我们实现小区间优化的思路为:

  1. 判断小区间数组是否小于10个数.
  2. 如果区间不小于10,则执行快排逻辑.
  3. 如果区间小于等于10,则执行直接插入排序逻辑.

综上所述,小区间代码实现如下:

 

递归函数有以下几个缺点:

  • 内存消耗大递归调用会占用大量的内存空间,因为每次调用都需要在内存中保存当前的状态和参数。

  • 性能低下递归调用会增加函数调用的开销,因此在一些情况下会导致程序的性能下降。

  • 可读性差递归函数通常比较复杂,难以理解和调试,降低了代码的可读性。

  • 可能导致栈溢出如果递归调用层次过深,会导致栈溢出的问题,使程序崩溃

  • 难以优化一些编译器和优化工具难以对递归函数进行有效的优化,导致性能不佳。


  1. 直接改为循环.(如:斐波那契数列)
  2. 利用栈辅助改为循环.(如:二叉树的前序遍历)

  1. 将初始数组区间压入栈
  2. 在栈里取一段区间,单趟排序
  3. 单趟分割子区间入栈
  4. 子区间只有一个值或着不存在就不入栈
  5. 重复步骤2-4,直到栈为空,则排序完成.

因为快排改非递归时要借助栈结构,因此我先将栈相关定义的头文件贴在这里,具体栈的C语言完整实现可以移步我的另一篇博客,在文末有数据结构栈实现的完整代码,大家可以直接粘贴过来使用:

(注:如果本身没有自己实现数据结构栈的工程文件的,一定要将该博客末尾的Stack.h文件和Stack.c文件粘贴在排序项目文件里才可以正常使用栈的相关功能,否则C语言是不支持直接使用的!)

 

综上,快排的非递归代码实现如下:

 

//主要解决快速排序面对大量重复数据时效率低下的问题

//该部分内容待补


希望这篇快速排序算法详解能对大家有所帮助,欢迎大佬们留言或私信与我交流.

有关更多排序相关知识可以移步:

学海漫浩浩,我亦苦作舟!关注我,大家一起学习,一起进步!

 相关文章推荐

【数据结构】八大排序之冒泡排序算法

【数据结构】八大排序之希尔排序算法

【数据结构】八大排序之直接插入排序算法

【数据结构】八大排序之简单选择排序

【数据结构】八大排序之堆排序算法

【数据结构】八大排序之快速排序算法

【数据结构】八大排序算法之归并排序算法

【数据结构】八大排序之计数排序算法


 数据结构排序篇思维导图:


    以上就是本篇文章【【数据结构】八大排序之快速排序算法】的全部内容了,欢迎阅览 ! 文章地址:http://ww.kub2b.com/news/10055.html
     栏目首页      相关文章      动态      同类文章      热门文章      网站地图      返回首页 企库往资讯移动站 http://ww.kub2b.com/mobile/ , 查看更多   
最新文章
环球圆桌对话:用反制告诉美方,霸道高关税是错的
编者按:近日,美国借“对等关税”的名义挑动全球范围的“关税战”,引起国际舆论关注。中国为什么必须就“对等关税”实施反制?
OPPO、ViVO、加多宝的品牌营销强在哪里?oppo手机是哪个国家的品牌「OPPO、ViVO、加多宝的品牌营销强在哪里?」
今天跟大家分享品牌营销,它有规律可循。▌一、营销的品牌导向1.企业的品牌导向:创业的时候,开始的时候是产品导向,还是品牌导
手机静态ip设置参数 这七步帮你完成手机静态ip「手机静态ip设置参数 这七步帮你完成」
手机在我们现在飞速发展的社会中有着十分重要的作用,随着互联网的发展,手机的速度也是越来越快,越来越流畅。但也有时候我们在
tplogin重新设置密码,tplogincn路由器设置管理密码是多少tplogincn手机登录「tplogin重新设置密码,tplogincn路由器设置管理密码是多少」
tplogincn路由器路由器的管理密码:1.一般路由器的管理账号和密码是:admin(小写字母)。2.有些路由器要求安全登录一次,并设置自己
vivo 是什么手机牌子?认识一款手机-VIVOvivo中文叫什么手机「vivo 是什么手机牌子?认识一款手机-VIVO」
vivo,一个从音乐手机起步,逐渐成长为全球知名品牌,在智能手机领域不断追求创新和完美的品牌。从最初的步步高音乐手机,到如今
游戏手机的自我救赎:ROG 8 Pro上手后,我看到了ROG的未来专门打游戏的手机「游戏手机的自我救赎:ROG 8 Pro上手后,我看到了ROG的未来」
来源|锚思科技作者|陈宝玉 游戏手机二选一,告诉你我的选择!!! 游戏手机作为手机的一个细分产品线,只有专业玩家和对游戏有
battery guru最新版 v2.3.13手机电池检测软件「battery guru最新版 v2.3.13」
battery guru最新版是一款能够对你安卓设备的电池进行保护,能够延长其使用寿命。多项功能的设置,让你能够通过更为精准的数据,
CBA1/4决赛:辽篮拿到赛点,青岛队扳平比分
4月15日,2024-2025赛季中国男子篮球职业联赛(CBA)季后赛四分之一决赛继续进行,首回合失利的青岛队客场大胜广厦队将总比分扳
单场0分又被雪藏!火箭队第18人恐难留队,三分精准,但功能单一
火箭队季后赛的对手已然确定。北京时间4月16号,孟菲斯灰熊队客场不敌勇士队。如此一来,灰熊队还得与独行侠以及国王队的胜者进
《刺客信条:奥德赛》v1.5.0十四项修改器[MrAntiFun][Epic]刺客信条手机版下载「《刺客信条:奥德赛》v1.5.0十四项修改器[MrAntiFun][Epic]」
《刺客信条:奥德赛》v1.5.0十四项修改器,包含无限肾上腺素,无限技能点,完美潜行等等功能助你轻松“暗杀”!让你在希腊尽情无