【数据结构】排序——快速排序

news2025/2/26 5:47:41

前言

本篇博客我们继续介绍一种排序——快速排序,让我们看看快速排序是怎么实现的

💓 个人主页:小张同学zkf

⏩ 文章专栏:数据结构

若有问题 评论区见📝

🎉欢迎大家点赞👍收藏⭐文章 ​

目录

1.快速排序(hoare方法)

2.快速排序(挖坑法)

3.快速排序(前后指针法)

 4.快速排序(非递归法)

5.快速排序特性


 

1.快速排序(hoare方法)

快速排序是 Hoare 1962 年提出的一种二叉树结构的交换排序方法,其基本思想为: 任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右 子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止
// 假设按照升序对 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,本篇博客结束!!!

 

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

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

相关文章

Python高级(三)_正则表达式

Python高级-正则表达式 第三章 正则表达式 在开发中会有大量的字符串处理工作,其中经常会涉及到字符串格式的校验。 1、正则表达式概述 正则表达式,又称正规表示式、正规表示法、正规表达式、规则表达式、常规表示法(英语:Regular Expression,在代码中常简写为regex、…

springboot企业培训管理系统-计算机毕业设计源码50804

摘要 本研究旨在开发一套高效、安全、易用的springboot企业培训管理系统。该系统致力于提升企业的培训管理效率&#xff0c;确保培训质量与效果的持续优化。通过整合先进的AI技术&#xff0c;在企业培训管理中发挥着不可或缺的作用。 通过采用Java作为主要开发语言&#xff0c;…

AI Earth——2020年中国建筑物高度CNBH数据产品(10m)

数据介绍: 复旦大学生命科学学院GC3S团队(吴万本博士、赵斌教授等)利用多源地球观测数据和机器学习技术,构建了中国第一个10米分辨率的建筑高度估计模型(CNBH-10m)。基于此模型建立了中国10米分辨率的建筑高度数据集。此数据集基于全天候地球观测(雷达、光学和夜光图像)…

USB PD SINK协议取电芯片不同品牌介绍对比-在选择PD SINK 协议芯片时,用户需要综合考虑各方面的因素,包括工作耐压、稳定性、兼容性等

PD SINK协议芯片是现代通信技术中的一项重要技术&#xff0c;它起着连接不同系统的桥梁作用。协议芯片内部集成了各种不同的通信协议&#xff0c;如蓝牙、Wi-Fi、以太网等&#xff0c;使不同设备之间的通信变得更加简单和高效。协议芯片推动了信息通信技术的发展。在过去&#…

赋能 Web3 与 AI 的未来,TARS 协议如何重塑去中心化生态?

TARS 协议如何在 Web3 生态中引领 AI 技术的变革&#xff1f;作为新兴的模块化 AI 平台&#xff0c;TARS 通过整合先进的 AI 模型和区块链技术&#xff0c;为用户提供了更加智能、安全和高效的解决方案。让我们一起回顾第 16 期 TinTinAMA 的精彩内容吧&#xff01; 在 TinTinL…

1. openstack

openstack 一、云的简介1、优势2、类型2.1 根据提供的服务范围2.2 根据提供服务不同 二、openstack核心组件1、核心组件2、nova组件2.1 nova核心进程 3、glance组件4、cinder组件5、neutron组件6、swift组件7、cellometer组件8、keystone组件9、heat组件10、dashboard 一、云的…

超简单的通配证书签发工具,免费,无需安装任何插件到本地

常见的acme.sh 或者 lego等工具需要配置&#xff0c;安装不灵活&#xff0c;续签需要配置计划任务&#xff0c;签发单域名证书或者通配证书需要不同的指令和配置&#xff0c;繁琐&#xff0c;如果自己程序想要对接签发证书的api有的不支持&#xff0c;有的用起来繁琐。 最近发…

性价比高充电宝有哪些?充电宝十大最佳品牌大盘点!

在如今这个高度数字化的时代&#xff0c;我们的生活离不开各种电子设备&#xff0c;而充电宝作为保障电子设备续航的重要工具&#xff0c;其地位日益凸显。然而&#xff0c;面对市场上琳琅满目的充电宝品牌和产品&#xff0c;要挑选到一款性价比高的充电宝并非易事。在这篇盘点…

本地部署,Colorizer: 让黑白图像重现色彩的奇迹

目录 引言 什么是 Colorizer ​编辑​编辑 Colorizer 的特点 工作原理 应用场景 本地部署 本地运行 实验与结果 结语 Tip&#xff1a; 引言 自摄影术发明以来&#xff0c;黑白图像一直是记录历史和艺术创作的重要手段。然而&#xff0c;黑白图像虽然具备其独特的美…

idea中打开静态网页端口是63342而不是8080

问题&#xff1a; 安装了tomcat 并且也配置了环境&#xff0c;但是在tomcat下运行&#xff0c;总是在63342下面显示。这也就意味着&#xff0c;并没有运行到tomcat环境下。 找了好几个教程&#xff08;中间还去学习了maven&#xff0c;因为跟的教程里面&#xff0c;没有maven,但…

2024年国企都在用哪些eHR人事系统?(附国企HR系统功能分享)

近年来&#xff0c;国家也一直在寻求国企改革上的突破。但是部分国有企业在经营过程中&#xff0c;出于历史和现实的多重影响&#xff0c;人力资源管理工作迟迟没有较大进展&#xff0c;还存在诸多不适应现代企业发展的弊端存在。随着科技进步&#xff0c;许多国企正转向创新的…

【昇思25天学习打卡营打卡指南-第二十三天】Pix2Pix实现图像转换

Pix2Pix实现图像转换 Pix2Pix概述 Pix2Pix是基于条件生成对抗网络&#xff08;cGAN, Condition Generative Adversarial Networks &#xff09;实现的一种深度学习图像转换模型&#xff0c;该模型是由Phillip Isola等作者在2017年CVPR上提出的&#xff0c;可以实现语义/标签到…

2024最新版pycharm安装激火教程,附安装包+激huo马,Python教程,pycharm安装包!!

PyCharm的安装 PyCharm 是一个专门为 Python 开发者设计的 IDE&#xff0c;它同样具有代码导航、重构、调试和分析等功能。PyCharm 支持多种项目类型&#xff0c;如普通项目、Python 测试项目、Django 项目等&#xff0c;并提供了大量的内置模板和插件&#xff0c;以帮助您更快…

【项目实战课】大语言模型提示词(Prompt)工程实战

欢迎大家来到我们的项目实战课&#xff0c;本期内容是《大语言模型提示词&#xff08;Prompto&#xff09;工程实战》。所谓项目课&#xff0c;就是以简单的原理回顾详细的项目实战的模式&#xff0c;针对具体的某一个主题&#xff0c;进行代码级的实战讲解。 本课程内容 提示词…

自动化(二正)

Java接口自动化用到的技术栈 技术栈汇总&#xff1a; ①Java基础&#xff08;封装、反射、泛型、jdbc&#xff09; ②配置文件解析(properties) ③httpclient&#xff08;发送http请求&#xff09; ④fastjson、jsonpath处理数据的 ⑤testng自动化测试框架重点 ⑥allure测试报…

cuda安装使用问题,print(torch.cuda.is_available()),仍然输出false,如何解决?

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

springboot it职业生涯规划系统lw源码调试演示视频

第3章 系统分析 面对即将开发的系统&#xff0c;进行提前的分析是必要的。这也是开发流程中必须有的环节。通常分析系统期间&#xff0c;主要涉及的内容包括系统开发可行性问题&#xff0c;对系统功能和性能的分析等问题。 3.1 可行性分析 在正式对需要建设的项目进行投资前&…

UNI_App平台调试指南 debug(十五)

App平台调试指南 debug 常规开发里,在 HBuilderX 的运行菜单里运行 App,手机端的错误或 console.log 日志信息会直接打印到控制台。 如果需要更多功能,比如审查元素、打断点 debug,则需要启动调试模式。自 HBuilderX 2.0.3+ 版本起开始支持 App 端的调试。 #打开调试窗口…

Transformer特辑

https://github.com/LongxingTan/Machine-learning-interview 模型结构 基本单元&#xff1a;token_embedding positional encoding, encoder, token_embedding positional encoding, decoderencoder: (self-attention, skip-connect, ln), (ffn, skip-connect, ln)decoder:…

为什么说精益变革失败的本质是企业丧失自我批评能力?

在商界的浩瀚星空中&#xff0c;那些曾经熠熠生辉的成功企业&#xff0c;有时也难逃衰落的命运。当它们陷入困境&#xff0c;尝试通过精益变革来重振旗鼓时&#xff0c;往往面临诸多挑战。然而&#xff0c;仔细观察这些企业的变革过程&#xff0c;我们不难发现&#xff0c;精益…