深度理解排序算法——快速排序

这篇具有很好参考价值的文章主要介绍了深度理解排序算法——快速排序。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在如今所知的众多排序算法中,快速排序无疑是脱颖而出的一种高效排序算法,在众多的情景下快速排序的算法效率都是数一数二的。闲话少叙,直接开始讲解快速排序的本质。

霍尔(Hoare)法:

霍尔版本作为快速排序的最初版本,其他的版本都在霍尔版本的基础上衍生出来,因此掌握好霍尔版本的快速排序显得尤为重要。
霍尔版本排序的内容:规定一个比较数key(key常常取两端下标,本文以左端为例)和一段闭区间[left,right],先让right向前遍历至小于key的值,二者交换,再让left向前遍历至大于key的值,二者交换,循环直至left=right,将left(right)处的值与key值交换
图解:
深度理解排序算法——快速排序,排序算法,算法,c语言,数据结构
当我们把第一遍排序了解清楚后,自然会想到6左边的元素和6右边的元素要向排好序,只需要重复第一遍排序的原理,而这就可以通过递归来实现,即每一次排好序,我们都可以返回left的-1值作为左区域的末尾,+1值作为右区域的起始位置
OK,那么接下来我们就来探讨一下递归的结束条件
深度理解排序算法——快速排序,排序算法,算法,c语言,数据结构
极端情况分析:*假设给定的数组最左端为0,其余元素均是正数,当right指针向前遍历的时候会由于找不到比a[key]小的元素而导致越界访问(left反之同理),故而我们必须时刻判断left和right的大小关系,避免越界,如果出现极端情况,即此次排序对数组不做任何变化。

//代码演示:
int _QuickSort(int* a,int left,int right){
	int key=left;
	while(left<right){
		while(left<right && a[right]>=a[key])
			--right;
		while(left<right && a[left]<=a[key])
			++left;
		Swap(&a[left],&a[right]);//Swap代表交换两个值
		}
	Swap(a[key],a[left]);
	return left;}
void QuickSort(int* a,int left,int right){
	if(left>=right)
		return;
	int keyi=_QuickSort(a,left,right);
	QuickSort(a,left,keyi-1);
	QuickSort(a,keyi+1,right);}
			

//

挖坑法:

最初的霍尔版本在初步理解上可能难以理解,由于在逻辑上线性关系不强,因此有人衍生出了一种非常容易理解的快排版本,俗称——挖坑法

本质:假设选定最左端的数为坑,将其元素寄存到一个key临时变量中,右指针right向前遍历找到小于key的元素放入坑中,此元素原位置变为新的坑,再移动左指针找到大于key的元素放入坑中,直至left与right相遇,将key中存储的元素放入坑中,完成一次排序。剩下的就是让递归重复解决子问题了。
图解:
深度理解排序算法——快速排序,排序算法,算法,c语言,数据结构深度理解排序算法——快速排序,排序算法,算法,c语言,数据结构

//代码演示:
int _QuickSort(int* a,int left,int right){
	int key=a[left];
	int hole=left;//坑
	while(left<right){
		while(left<right && a[right]<=key)
			{--right;a[hole]=a[right];hole=right;}
		while(left<right && a[left]>=key)
			{++left;a[hole]=a[left];hole=left;}
		}
	a[hole]=key;
	return hole;}
void QuickSort(int* a,int left,int right){
	if(left<=right)
		return;
	int keyi=_QuickSort(a,left,right);
	QuickSort(left,keyi-1);
	QuickSort(keyi+1,right);}
		

//

双指针法:

定义两个指针分别为prev和cur,对于第一遍排序,prev=0,cur=1,指定最左端元素为比较数key。当在cur未越界的情况下,只要cur所指向的元素大于等于key所指向的元素,则cur向后移动1个单位,prev不做变化,反之交换cur与prev+1所指向的元素
图解:深度理解排序算法——快速排序,排序算法,算法,c语言,数据结构
不难发现:
1、最开始prev和cur相邻的
2、当cur遇到比key的大的值以后,他们之间的值都是比key大的值
3、cur找小,找到小的以后,跟+ +prev位置的值交换相当于把大翻滚式往右边推,同时把小的换到左边

//代码演示:
int _QuickSort(int* a,int left,int right){
	int key=left,prev=left;int cur=prev+1;
	while(cur<right){//未越界
		if(a[cur]<a[key] && ++prev!=cur)//优化算法,
			Swap(&a[prev],&a[cur]);		//自身交换等价于不做变化
		cur++;}
	Swap(&a[prev],&a[key]);
	return prev;}
void QuickSort(int * a,int left,int right){
	if(left>=right)
		return;
	int keyi=_QuickSort(a,left,right);
	QuickSort(a,left,keyi-1);
	QuickSort(a,keyi+1,right);}

迭代法:

顾名思义就是不采取递归的方式进行排序,通常借助栈的结构来实现
仿照递归的逻辑,先对整体进行一次排序,相当于一次入栈操作,(一次入栈两个数据,分别代表左右两端【区间】,先入的是右端,后入的是左端),再一次取出两个元素进行排序,一次排序结束后再先后压入右区域的区间和左区域的区间(必须按照这样的顺序——因为栈是先入后出的,后压入左区域的区间才会先出栈先处理

//代码演示:
int _QuickSort(int* a,int left,int right){
	int key=left;
	while(left<right){
		while(left<right && a[right]>=a[key])
			--right;
		while(left<right && a[left]<=a[key])
			++left;
		Swap(&a[left],&a[right]);//Swap代表交换两个值
		}
	Swap(a[key],a[left]);
	return left;}
void QuickSort(int* a,int begin,int end){
	Stack st;
	StackInit(&st);
	StackPush(&st,end);StackPush(&st,begin);
	while(!StackEmpty(&st)){//栈为空时代表排好序了
		int left=StackFront(&st);
		StackPop(&st);
		int right=StackFront(&st);
		StackPop(&st);
		int keyi=_QuickSort(a,left,right);
		if(keyi+1<right)//不成立时代表单个元素或空集
		{StackPush(&st,right);StackPush(&st,keyi+1);}
		if(left<keyi-1)
		{StackPush(&st,keyi-1);StackPush(&st,left);}
		}
	StackDestroy(&st);}

时间复杂度及空间复杂度:

由于递归需要建立栈帧,因此执行的时间与开辟栈帧大小密切相关,下面分析两种极端情况:

递归展开图呈现完全二叉树的形状:此时的空间复杂度即为O(logN),单排一次的时间复杂度时O(N),故而总时间复杂度为O(NlogN)
**递归展开图不存在度为2的节点:**此时的空间复杂度为O(N),那么总时间复杂度为O(N^2)

这么看的话,貌似快速排序的时间复杂度为O(N^2),看上去是一种效率很低的算法。不然,此种极端只出现在数组基本有序的情况,而基本有序的时候我们并不采用快速排序,日常中见到的大部分数据都是非常无序的,这些情况下快速排序的效率是非常高的,故而业内常常把快速排序的平均时间NlogN作为快排的时间复杂度

但这也意味着快速排序有值得优化的地方,从逻辑图上来讲只需要让所建立的二叉树高度尽量小就可以提高计算效率,因此衍生出了一种取中值法,即不直接将left作为key,而是将left right mid三个对应值取中间值交换到最左端作比较数

//

Over!文章来源地址https://www.toymoban.com/news/detail-771962.html

到了这里,关于深度理解排序算法——快速排序的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构与算法】十大经典排序算法-快速排序

    🌟 个人博客: www.hellocode.top 🏰 Java知识导航: Java-Navigate 🔥 CSDN: HelloCode. 🌞 知乎 :HelloCode 🌴 掘金 :HelloCode ⚡如有问题,欢迎指正,一起学习~~ 快速排序(Quick Sort)是一种高效的排序算法,是对冒泡排序的优化。它采用分治法(Divide and Conquer)的思想,将待排序序列

    2024年02月13日
    浏览(47)
  • 【数据结构与算法】:选择排序与快速排序

    🔥 个人主页 : Quitecoder 🔥 专栏 :数据结构与算法 我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:腾讯云 欢迎来到排序的第二个部分:选择排序与快速排序! 选择排序是一种简单直观的比较排序算法。该算法的基本思想是在每一轮中选出当前未排序部分的最

    2024年03月17日
    浏览(41)
  • 数据结构与算法:快速排序

    荷兰国旗问题 想要理解快速排序,就先理解这个问题: [LeetCode75.颜色分类] 荷兰国旗是由红白蓝三色组成的: 现在将其颜色打乱 然后根据一定的算法,将其复原为红白蓝三色,这就叫做荷兰国旗问题。 在LeetCode的题目中,其将荷兰国旗的三个颜色用0,1,2来表达,也就是说

    2024年01月15日
    浏览(36)
  • 【数据结构】排序算法(二)—>冒泡排序、快速排序、归并排序、计数排序

    👀 樊梓慕: 个人主页  🎥 个人专栏: 《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》 🌝 每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.冒泡排序 2.快速排序 2.1Hoare版 2.2占坑版 2.3前后指针版 2.4三数取中对快速排序的优化 2.5非递归版 3.归

    2024年02月08日
    浏览(35)
  • 数据结构与算法之快速排序

    快速排序 (Quick Sort),又称划分交换排序(partition-exchange sort),通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数

    2024年02月10日
    浏览(34)
  • 【数据结构与算法】:非递归实现快速排序、归并排序

    🔥 个人主页 : Quitecoder 🔥 专栏 :数据结构与算法 上篇文章我们详细讲解了递归版本的快速排序,本篇我们来探究非递归实现快速排序和归并排序 快速排序的非递归实现主要依赖于栈(stack)来模拟递归过程中的函数调用栈。递归版本的快速排序通过递归调用自身来处理子

    2024年03月24日
    浏览(44)
  • 【数据结构与算法】如何对快速排序进行细节优化以及实现非递归版本的快速排序?

    君兮_的个人主页 即使走的再远,也勿忘启程时的初心 C/C++ 游戏开发 Hello,米娜桑们,这里是君兮_,国庆长假结束了,无论是工作还是学习都该回到正轨上来了,从今天开始恢复正常的更新频率,今天为大家带来的内容是快速排序的两大优化和非递归实现 好了废话不多说,开

    2024年02月08日
    浏览(37)
  • 【数据结构】—从冒泡排序丝滑过度快速排序(含C语言实现)

                                            食用指南:本文在有C基础的情况下食用更佳                                          🔥 这就不得不推荐此专栏了: C语言                                        ♈️ 今日夜电波:靴の花火—ヨルシカ            

    2024年02月08日
    浏览(28)
  • 【数据结构】详解七大排序算法(直接插入排序、希尔排序、直接选择排序、堆排序、冒泡排序、快速排序)

    1、基本思想    把待排序的数按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所以的记录插入完为止,得到一个新的有序序列。    实际中我们玩扑克牌时,就用到了插入排序的思想 基本步骤:    当插入第i个元素时,前面的arr[0]、arr[2]…arr

    2024年02月04日
    浏览(52)
  • python算法与数据结构(搜索算法和拓扑排序算法)---深度优先搜索

    了解树/图的 深度遍历,宽度遍历 基本原理; 会使用python语言编写 深度遍历,广度遍历 代码; 掌握 拓扑排序算法 搜索引擎 提到搜索两个子,大家都应该会想到搜索引擎,搜索引擎的基本工作步骤; 网页爬取 — 数据预处理 — 排序 — 查询 第一步,网页爬取,非常重要,

    2024年01月20日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包