快速排序的实现(3种)

目录

  • 0.快速排序
  • 1.Hoare版本
    • 1.1基本思想
    • 1.2算法描述
    • 1.3画图解释
    • 1.4问题?
    • 1.5代码实现
  • 2.挖坑法
    • 2.1算法描述
    • 2.2画图解释
    • 2.3代码实现
  • 3.先后指针法
    • 3.1算法描述
    • 3.2画图解释
    • 3.3代码实现
  • 4.优化
    • 4.1优化方法
    • 4.2优化代码
  • 5.非递归实现快排
    • 5.1算法描述

0.快速排序

1.时间复杂度:O(N*logN)
稳定性:不稳定
整体综合性能和使用场景都比较好
空间复杂度:O(logN)

1.Hoare版本

1.1基本思想

通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

1.2算法描述

  • 从数列中选出一个元素作为基准(一般都是第一个元素)
  • 定义两个指针left,right。
  • right先走,找小,找到小就停下来;left后走,找大,找到大就停下来。然后交换left和right对应的值。
  • 重复上述操作,直到left和right相遇,停下来并交换相遇点的值和基准值。
  • 以基准值分成了两个区间,递归地重复上述操作。

1.3画图解释

第一遍:
right先走,找小;left后走,找大
left和right相遇时,交换相遇点和基准对应的值
达到了分区间作用;左区间 < 基准 ,右区间 > 基准

在这里插入图片描述

开始递归上述操作

在这里插入图片描述

1.4问题?

1.为什么要让right先走?如果left先走行不行?

让right先走最后可以满足比基准小的都在基准的左边,比基准大的都在基准的右边。
不行,如果left先走就不能满足上述条件
在这里插入图片描述

2.为什么left和right的相遇点对应的值一定小于等于基准?

left去遇见right:
right先走,找小,找到小就停下来;left后走,找大,找到大就停下来,两者对应的值发生交换;如果left没有找到与right发生相遇,所相遇点一定是小于基准的
right去遇见left:
right先走,找小,如果没有找到就一直走,直到和left相遇,此时相遇点所对应的值是小于基准的

1.5代码实现

// 快速排序hoare版本
void PartSort(int* a, int left, int right)
{
	if (left >= right)//表示该区间只有一个元素或者该区间不存在
		return;

	int begin = left;
	int end = right;
	int key = a[left];
	while (begin < end)
	{
		//先找小
		while (begin < end && a[end] > key)
			--end;
		//找大
		while (begin < end && a[begin] <= key)
			++begin;
		//交换begin和end位置的值
		swap(&a[begin], &a[end]);
	}
	//begin和end相遇
	swap(&a[begin], &a[left]);//swap(&a[begin,&key);两值会交换,但是系统并不知道key对应数组中第几个数据
	//[left,begin-1]  begin  [begin+1,right]
	PartSort12(a, left, begin - 1);
	PartSort12(a, begin + 1, right);
	
}

2.挖坑法

2.1算法描述

  • 把第一个数据存放在一个临时变量key中,形成坑位
  • right先走,找小,找到小就停下来
  • 将right所对应的值,放在坑位中,right形成新的坑位
  • left后走,找大,找到大就停下来
  • 将left所对应的值放在坑位中,left形成新的坑位
  • 当left和rihgt相遇时,两者都处在坑位位置,把临时变量key放到坑位中
  • 分区间递归

2.2画图解释

第一遍:
先将第一个数据放在一个临时变量(key)中形成坑位
right先走,找到小就放到坑位中去,right处形成新的坑位
left后走,找到大就放在坑位中去,left处形成新的坑位
分区间:左区间 < key ;右区间 > key

在这里插入图片描述

第二遍:递归重复上述操作

在这里插入图片描述

2.3代码实现

// 快速排序挖坑法
void PartSort2(int* a, int left, int right)
{
	//表示该区间只有一个元素或者该区间不存在
	if (left >= right)
		return;

	//创建一个临时变量存放第一个数据的值,形成坑位
	int key = a[left];
	int begin = left;
	int end = right;
	while (begin < end)
	{
		while (begin<end && a[end]>key)
			--end;
		a[begin] = a[end];
		while (begin < end && a[begin] <= key)
			++begin;
		a[end] = a[begin];
	}
	//begin和end相遇
	a[begin] = key;
	//[left,begin-1] begin [begin+1,right]
	PartSort2(a, left, begin - 1);
	PartSort2(a, begin+1, right);
}

3.先后指针法

3.1算法描述

  • 初始时,prev指到序列的开头,cur指到序列的下一个位置
  • 先判断cur位置对应的值和key(序列开头位置)的大小
  • 比key小 ++prev 将prev和cur所对应的值进行交换,然后++cur
  • 如果比key大,直接++cur
  • 当cur越界时,将key的值和prev的值进行交换

3.2画图解释

第一遍:prev指到序列的开头,cur指到序列的下一个位置
判断cur所对应的值与key的大小

在这里插入图片描述

递归重复上述操作

在这里插入图片描述

3.3代码实现

// 快速排序前后指针法
void PartSort3(int* a, int left, int right)
{
	//表示该区间只有一个数据或者该区间不存在
	if (left >= right)
		return;

	int begin = left;
	int end = right;
	int prev = begin;
	int cur = begin+1;
	int key = a[begin];
	while (cur <= end)
	{
		if (a[cur] < key)
		{
			++prev;
			swap(&a[cur], &a[prev]);
		}
		++cur;
	}
	//交换prev和key位置的值
	swap(&a[prev], &a[begin]);
	//[begin,prev-1] prev [prev+1,end]
	PartSort3(a, begin, prev - 1);
	PartSort3(a, prev+1,end);

}

4.优化

4.1优化方法

1.三数取中
2.小区间优化

1.为什么要进行三数取中?

当序列有序时,会加大快速排序所用的时间。分区间的时候,可能出现下面这种情况
在这里插入图片描述

int Mid(int* a, int left, int right)
{
	int mid = left + ((right - left) >> 1);
	if (a[left] > a[mid])
	{
		if (a[mid] > a[right])
			return mid;
		else if (a[left] > a[right])
			return right;
		else
			return left;
	}
	else//a[left]<a[mid]
	{
		if (a[right] > a[mid])
			return mid;
		else if (a[left] > a[right])
			return left;
		else
			return right;
	}
}

2.小区间优化

防止递归深度太深,栈溢出。同时可以减少所用时间。

4.2优化代码

void QuickSort(int* a, int left, int right)
{
	if (left >= right)
		return;

	
	//小区间优化
	//如果一个区间内的数据小于10个就进行小区间优化
	if ((right - left + 1) < 10)
	{
		InsertSort(a+left, right - left + 1);
	}
	else
	{
		//三数取中
		int mid = Mid(a, left, right);
		swap(&a[mid], &a[left]);

		int t = PartSort1(a, left, right);
		//[left,t-1] t [t+1,right]
		QuickSort(a, left, t - 1);
		QuickSort(a, t + 1, right);
	}
}

5.非递归实现快排

5.1算法描述

  • 建栈
  • 先将序列的left和right压入栈中
  • 再取出栈顶的left和right进行(挖坑法或前后指针法)
  • 再分区间把left和right压入栈中
  • 直到栈为空为止
    在这里插入图片描述

下面代码只有具体过程,没有与栈相关的代码

#include "Stack.h"
// 快速排序 非递归实现
void QuickSortNonR(int* a, int left, int right)
{
	ST st;
	StackInit(&st);
	//先压入right,再压入left
	StackPush(&st, right);
	StackPush(&st, left);

	while (!StackEmpty(&st))
	{
		int begin = StackGet(&st);
		StackPop(&st);
		int end = StackGet(&st);
		StackPop(&st);

		int keyi = PartSort1(a, begin, end);//分区间的点
			
		//[begin,keyi-1] keyi [keyi+1,end]
		//该区间不存在或者只有一个数据就不用压入栈中了
		if (begin < keyi - 1)
		{
			StackPush(&st,keyi - 1);
			StackPush(&st, begin);
		}
		if (keyi + 1 < end)
		{
			StackPush(&st,end);
			StackPush(&st, keyi + 1);
		}
	}
	StackDestory(&st);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AGV选型要点及步骤,保证企业选择的AGV小车更实用

AGV AGV小车作为智能化物流仓储不可或缺的工具&#xff0c;在制造业得到了广泛的应用&#xff0c;市场需求呈现出井喷式增长。但是AGV市场还存在着很多问题&#xff0c;制造企业在产品选型时往往缺乏正确的引导。 AGV智能仓储 毫无疑问,我们的自动化物流系统已离不开AGV小车了,…

Spring Boot + Vue 全栈开发,都需要哪些前端知识?

Node.js默认安装的npm包和工具的位置&#xff1a;Node.js目录\node_modules 在这个目录下你可以看见 npm目录&#xff0c;npm本身就是被NPM包管理器管理的一个工具&#xff0c;说明 Node.js已经集成了npm工具 #在命令提示符输入 npm -v 可查看当前npm版本 npm -v 二、使用n…

【Android】Android Studio 使用Kotlin写代码时代码提示残缺问题解决

问题描述 Android Studio升级之后&#xff0c;从Android Studio 4.2升级到Android Studio Arctic Fox版本&#xff0c;因为项目比较老&#xff0c;使用的Gradle 版本是3.1.3&#xff0c;这个版本的Android Studio最低支持Gradle 3.1版本&#xff0c;应该算是比较合适的版本。 …

天猫超市卡有什么用?

有时候不知道在网上买什么的时候&#xff0c;恰好就会收到一些猫超卡、京东卡类的礼物 这真是让人苦恼 还好收卡云一直在收这些礼品卡券啊&#xff0c;不然我的大部分礼品卡最后只有过期的份了&#xff01;

SpringBoot异常处理机制之自定义404、500错误提示页面 - 518篇

历史文章&#xff08;文章累计500&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 《…

c++ 正则匹配得使用

标头&#xff1a;#include <regex> 相关函数&#xff1a; regex_match regex_replace regex_search 名称描述regex_match测试正则表达式是否与整个目标字符串相完全匹配。regex_replace替换匹配正则表达式。regex_search搜索正则表达式匹配项。 1. regex_search 成功搜…

ctr/cvr预估之WideDeep模型

ctr/cvr预估之Wide&Deep模型 在探索点击率&#xff08;CTR&#xff09;和转化率&#xff08;CVR&#xff09;预估的领域中&#xff0c;我们始终追求的是一种既能捕获数据中的线性关系&#xff0c;又能发现复杂模式的模型。因子分解机&#xff08;Factorization Machines, …

张量在人工智能中的解释?

张量在人工智能中的解释&#xff1f; 张量是一种多维数组&#xff0c;它可以看作是向量和矩阵的推广。在人工智能领域&#xff0c;张量被用作数据的基本表示形式&#xff0c;尤其在深度学习中扮演着核心角色。张量的多维性允许它们表示复杂的数据结构和关系&#xff0c;而其可…

iOS之如何创建.framework静态库

番外&#xff1a;想要查看如何创建.a静态库可前往看我iOS之如何创建.a静态库-CSDN博客这篇文章。 一、创建framework项目 创建framework工程要选择iOS --> Cocoa Touch Framework输入项目名称PrintFramework也是编译生成的framework的名称。framework的名称也可以以后在项目…

不翻墙安装yolov8环境下的RT-DETR并实现PCB表面缺陷检测

目录 一、新建conda环境二、安装yolov8环境1.克隆安装包2.安装依赖包3.测试模型 任务2&#xff1a;基于RT-DETR实现PKU-PCB表面缺陷检测数据准备 数据增强测试 总结 一、新建conda环境 创建并激活conda环境&#xff1a; 在conda创建一个名为yolov8的新环境&#xff0c;并在其中…

React尚硅谷115-126(setState、Hooks、Fragment、context、组件优化、renderprops

122&#xff0c;context 只能用value传&#xff0c;可以传对象&#xff0c;字符串 一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信 使用&#xff1a; 创建Context容器对象&#xff1a; const XxxContext React.createContext() 渲染子组时&#xff0c;外面包…

SpringBoot的Web开发支持【超详细【一篇搞定】果断收藏系列】

Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println(“MyInterceptor.afterCompletion”); } } 使用Java的形式配置拦截器的拦截路径 在WebMvcConfig…

零门槛用AI,302.AI让人工智能变得简单易用

当下人工智能火爆&#xff0c;提到AI&#xff0c;几乎每个人都能说上几句&#xff0c;但是你真的会使用AI吗&#xff1f; 当涉及到如何实际使用AI时&#xff0c;许多人可能会觉得它太过高深莫测&#xff0c;从而产生一种距离感&#xff0c;不知如何开始。我和大家也一样&#x…

Apple - Game Center Programming Guide

本文翻译整理自&#xff1a;Game Center Programming Guide&#xff08; Updated: 2016-06-13 https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/GameKit_Guide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008304 文章…

jemeter基本使用

后端关验签&#xff0c;设置请求头编码和token 配置编码和token

【分布式事务】Seata AT实战

目录 Seata 介绍 Seata 术语 Seata AT 模式 介绍 实战&#xff08;nacos注册中心&#xff0c;db存储&#xff09; 部署 Seata 实现 RM 实现 TM 可能遇到的问题 1. Seata 部署成功&#xff0c;服务启动成功&#xff0c;全局事务不生效 2. 服务启动报错 can not get …

c++分隔字符串

可以使用getline函数。 有两个版本&#xff1a; 至于为什么可以使用getline函数返回值作为while的判断条件&#xff0c;cprimer中表述如下&#xff1a;

[MySql]两阶段提交

文章目录 什么是binlog使用binlog进行恢复的流程 什么是redolog缓冲池redologredolog结构 两阶段提交 什么是binlog binlog是二进制格式的文件&#xff0c;用于记录用户对数据库的修改&#xff0c;可以作用于主从复制过程中数据同步以及基于时间点的恢复&#xff08;PITR&…

Docker(六)-本地镜像发布到私有库

1.下载镜像Docker Registry 用于搭建私人版本Docker Hub docker pull registry2.运行私有库Registry 运行私有库Registry&#xff0c;相当于本地有个私有Docker hubdocker run -d -p hostPort:containerPort -v 【宿主机目录】:【容器目录】 --privilegedtrue 【私有库镜像】…

C++使用Poco库封装一个FTP客户端类

0x00 Poco库中 Poco::Net::FTPClientSession Poco库中FTP客户端类是 Poco::Net::FTPClientSession , 该类的接口比较简单。 上传文件接口&#xff1a; beginUpload() , endUpload() 下载文件接口&#xff1a; beginDownload() , endDownload() 0x01 FTPCli类说明 FTPCli类…