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

news2025/1/20 1:58:09

在这里插入图片描述

🎬 鸽芷咕:个人主页

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

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

📋 前言

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

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

相关文章

处理urllib.request.urlopen报错UnicodeEncodeError:‘ascii‘

参考&#xff1a;[Python3填坑之旅]一urllib模块网页爬虫访问中文网址出错 目录 一、报错内容 二、报错截图 三、解决方法 四、实例代码 五、运行截图 六、其他UnicodeEncodeError: ascii codec 问题 一、报错内容 UnicodeEncodeError: ascii codec cant encode charac…

缓存和缓冲的区别

近期被这两个词汇困扰了&#xff0c;感觉有本质的区别&#xff0c;搜了一些资料&#xff0c;整理如下 计算机内部的几个部分图如下 缓存&#xff08;cache&#xff09; https://baike.baidu.com/item/%E7%BC%93%E5%AD%98 提到缓存&#xff08;cache&#xff09;&#xff0c;就…

【LeetCode-剑指offer】--3.比特位计数

3.比特位计数 class Solution {public int[] countBits(int n) {int[] bites new int[n 1];for(int i 0 ; i < n;i){bites[i] Count(i);}return bites;}public int Count(int x){int count 0;while(x > 0){x & (x - 1);count;}return count;} }

【SVN】Windows版合并提交bat文件+自定义菜单快捷键

【工具向】利用bat批处理打开TortoiseGit简化版本管理流程_tortoisegit bat-CSDN博客 start cmd /k "cd C:\YourBranchProj && svn cleanup && svn update && svn merge C:\YourTrunkProj -r 历史版本号:HEAD && svn commit -m "me…

项目接口性能优化方案

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;CSDN实力新星&#xff0c;后端开发两年经验&#xff0c;曾担任甲方技术代表。会点点Java相关技术栈、帆软报表、低代码平台快速开发。技术尚浅&#xff0c;闭关学习中 &#x1f60…

论文阅读《Restormer: Efficient Transformer for High-Resolution Image Restoration》

论文地址:https://openaccess.thecvf.com/content/CVPR2022/html/Zamir_Restormer_Efficient_Transformer_for_High-Resolution_Image_Restoration_CVPR_2022_paper.html 源码地址:https://github.com/swz30/Restormer 概述 图像恢复任务旨在从受到各种扰动(噪声、模糊、雨滴…

低延时视频技术的应用场景和挑战

编者按 无线网络对人们的生活产生了巨大的影响&#xff0c;而5G技术的引入将彻底改变我们与世界互联互通的方式。在5G时代&#xff0c;实现万物互联离不开低延时技术的应用。 LiveVideoStackCon 2023 深圳站邀请到秒点科技的CEO扶凯&#xff0c;为大家分享低延时技术在物联网、…

家政行业的小程序都需要具备哪些功能?

家政服务小程序&#xff0c;覆盖多城&#xff0c;在线派单 适合行业&#xff1a;家电维修、家政保洁、养生护理、美容美发、预约服务上门等 系统功能&#xff1a;服务管理、商品管理、拼团/秒杀、订单管理、会员管理、派单管理、师傅管理、商家/服务点、财务管理、城市代理、次…

Python算法例30 统计前面比自己小的数

1. 问题描述 给定一个整数数组&#xff08;数组大小为n&#xff0c;元素的取值范围为0~10000&#xff09;&#xff0c;对于数组中的每个元素&#xff0c;计算其前面元素中比它小的元素数量。 2. 问题示例 对于数组[1&#xff0c;2&#xff0c;7&#xff0c;8&#xff0c;5]&…

分享44个PyQt5源码总有一个是你想要的

分享44个PyQt5源码总有一个是你想要的 学习知识费力气&#xff0c;收集整理更不易。 知识付费甚欢喜&#xff0c;为咱码农谋福利。 链接&#xff1a;https://pan.baidu.com/s/1_5H_0Ydg0XUa1fz5Jok51Q?pwd6666 提取码&#xff1a;6666 项目名称 B站直播弹幕姬&#xff…

快速、安全、高效地传输海量小文件

随着互联网技术的不断进步&#xff0c;我们正迈入信息爆炸的时代。在这个时代&#xff0c;企业每天都需要在互联网上传输海量的小文件。与传输常见的大文件相比&#xff0c;海量小文件的传输变得更加困难。接下来&#xff0c;我们将分析海量小文件传输面临的挑战&#xff0c;并…

云原生机器学习平台cube-studio开源项目及代码简要介绍

1. cube-studio介绍 云原生机器学习平台cube-studio介绍&#xff1a;https://juejin.cn/column/7084516480871563272 cube-studio是开源的云原生机器学习平台&#xff0c;目前包含特征平台&#xff0c;支持在/离线特征&#xff1b;数据源管理&#xff0c;支持结构数据和媒体标…

Modbus RTU转Modbus TCP模块,RS232/485转以太网模块,YL102 多功能串口服务器模块

特点&#xff1a; ● Modbus RTU协议自动转换成Mobus TCP协议 ● 100M高速网卡&#xff0c;10/100M 自适应以太网接口 ● 支持 AUTO MDI/MDIX&#xff0c;可使用交叉网线或平行网线连接 ● RS232波特率从300到256000可设置 ● 工作方式可选择TCP Server, TCP Client, U…

中间件系列 - Redis入门到实战(原理篇)

前言 学习视频&#xff1a; 黑马程序员Redis入门到实战教程&#xff0c;深度透析redis底层原理redis分布式锁企业解决方案黑马点评实战项目 中间件系列 - Redis入门到实战 本内容仅用于个人学习笔记&#xff0c;如有侵扰&#xff0c;联系删除 学习目标 Redis数据结构Redis网…

企业员工2024年工作计划和目标怎么写?怎么提醒自己按时执行?

2024年的钟声即将敲响&#xff0c;对于众多企业员工而言&#xff0c;新的一年意味着新的挑战和机遇。而在这之前&#xff0c;制定一份明确的2024年工作计划与目标就显得尤为重要。但不少员工在面对这个任务时&#xff0c;往往感到无从下手&#xff0c;那么如何撰写一份实用且有…

pyCharm 打印控制台中文乱码解决办法

解决方法 在 "File" -> "Settings" 中的控制台设置&#xff1a; 在 "File" -> "Settings" 中&#xff0c;你可以找到 "Editor" -> "General" -> "Console"。在这里&#xff0c;你可能会找到…

frp(实现内网穿透)服务搭建与ssh连接测试

frp 内网穿透 内网穿透的英文叫做NAT traversal&#xff0c;又被称为端口映射或内网映射&#xff0c;内网穿透是网络连接术语&#xff0c;如下图如果我&#xff08;局域网A中的一台服务器&#xff09;想访问另一个局域网c中的一台服务器&#xff0c;可以通过拥有公网ip的B服务…

自动备份B站Up主最新视频到百度网盘的Python脚本详解

自动备份B站Up主最新视频脚本详解&#xff08;Win和Linux有些不同&#xff09; 前言&#xff1a; 次篇文章启发于某些大胆的UP主&#xff08;老马&#xff09;的多次被封&#xff0c;并被下架一些视频。有些人并不能及时观看到&#xff0c;故写一个脚本自动下载最新视频。 &am…

Google Chrome 现在会在后台扫描泄露的密码

谷歌表示&#xff0c;Chrome 安全检查功能将在后台运行&#xff0c;检查网络浏览器中保存的密码是否已被泄露。 如果桌面用户正在使用标记为危险的扩展程序&#xff08;从 Chrome Web Store 中删除&#xff09;、最新的 Chrome 版本&#xff0c;或者如果启用安全浏览来阻止 Go…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之线性布局容器Row组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之线性布局容器Row组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Row组件 沿水平方向布局容器。 子组件 可以包含子组件。 接口 Row(…