当前位置: 首页 > news >正文

【排序】快速排序详解

✨✨欢迎大家来到Celia的博客✨✨

🎉🎉创作不易,请点赞关注,多多支持哦🎉🎉

所属专栏:排序

个人主页:Celia's blog~

一、快速排序的思想

快速排序的核心思想是:

  1. 选定一个key值作为基准值(一般是整个数组的第一个元素)。
  2. 把整个数组中比key小的元素放到key的左边,比key大的元素放到key的右边。这需要通过一次单趟排序来实现。
  3. 单趟排序结束后,可以认为先前选定的key值的位置已经排好序了。根据这个key值的位置,将数组分为左右两个子数组,再分别进行单趟排序(重复2过程)。直到左右数组不能再分为止。

二、快速排序的原理分析

想要实现快速排序,最重要的就是实现单趟排序,单趟排序主要有三个方法:霍尔法、挖坑法、前后针法。

2.1 霍尔法

霍尔法的思想是:

  1. 定义左右两个指针分别指向当前数组的首尾两边。
  2. 右指针先走,从右往左找到首个比key值小的元素。
  3. 再让左指针走,从左往右找到首个比key值大的元素。
  4. 交换左右指针所指向的两个元素。
  5. 重复2、3步骤,直到左右指针相遇
  6. 将key值所在的位置与左右指针相遇的位置的元素交换。
  7. 单趟排序结束,key值所在的位置左边都比key值小,右边都比key值大。

还有一个很重要的问题,为什么在单趟排序之后,两个指针相遇位置的元素值一定比key小呢?

  • 如果左指针遇到右指针,由于右指针是先走的,说明右指针已经找到了比key小的元素。
  • 如果右指针遇到左指针由于上一轮的交换,比key小的元素已经换到了当前左指针的位置,左指针的位置的元素一定也比key小。


结论:如果使用霍尔法进行单趟排序,只需要让与基准值(key)所在方位相反的指针先走就可以了。

2.2 挖坑法

挖坑法的思想是:

  1. 定义左右两个指针,分别指向数组的首尾位置。
  2. 选定一个基准值key(一般是数组的第一个元素),并记录。将当前基准值位置记作“坑”。
  3. 右指针先走,从右往左找到比key值小的元素。
  4. 将右指针所在的位置的元素移动到“坑”中,当前右指针所在的位置形成新的“坑”
  5. 左指针再走,从左往右找到比key值大的元素。
  6. 将左指针所在的位置的元素移动到“坑”中,当前左指针所在的位置形成新的“坑”
  7. 当左右指针相遇时,将key值填入左右指针相遇的位置。单趟排序结束。

这个方法相比于霍尔法更好理解,也不用考虑两指针相遇时的元素是否小于key的问题。 该方法效率与霍尔法相同

2.3 前后指针法

前后指针法的思想是:

  1. 选定一个基准值,用key保存起来。
  2. 定义prev指针指向数组首位置,定义cur指向prev的下一个位置
  3. 比较cur位置的元素与key的大小关系,若cur位置元素比key大,cur++。若cur位置元素比key小,先让prev++,再交换cur和prev位置的元素。
  4. 当cur大于数组大小时,结束遍历,将key所在的位置的值和prev所在位置的值交换。

三、快速排序的代码实现

3.0 核心代码逻辑

void Swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}
void QSort(int* a, int left, int right)
{if (left >= right)//递归结束条件return;int begin = left, end = right;//使用三种方法的其中一种进行单趟排序int mid = Part3(a, begin, end);//记录每一次排好的元素下标QSort(a, begin, mid - 1);//递归左右子数组QSort(a, mid + 1, end);
}

递归调用会将整个数组不断地分为两个子数组,如果递归传入的left和right相等,不用进行排序,如果left大于right,不符合区间的逻辑,也不需要排序。所以递归的结束条件为 left >= right

3.1 霍尔法

//霍尔法
int Part1(int* a, int left, int right)
{int keyi = left;int begin = left, end = right;while (begin < end){while (begin < end && a[end] >= a[keyi]){end--;}while (begin < end && a[begin] <= a[keyi]){begin++;}Swap(&a[begin], &a[end]);}Swap(&a[keyi], &a[begin]);return begin;
}

3.2 挖坑法

//挖坑法
int Part2(int* a, int left, int right)
{int key = a[left];int hole = left;int begin = left, end = right;while (begin < end){while (begin < end && a[end] >= key){end--;}a[hole] = a[end];hole = end;while (begin < end && a[begin] <= key){begin++;}a[hole] = a[begin];hole = begin;}a[hole] = key;return hole;
}

3.3 前后指针法

//前后指针法
int Part3(int* a, int left, int right)
{int keyi = left;int prev = left;int cur = prev + 1;while (cur <= right){if (a[cur] <= a[keyi]){prev++;Swap(&a[cur], &a[prev]);}cur++;}Swap(&a[keyi], &a[prev]);return prev;
}

四、快速排序的时间复杂度和空间复杂度分析

4.1 时间复杂度

  • 快速排序最核心的步骤是对每个子数组进行遍历比较操作,所以我们用遍历的次数来近似时间复杂度。
  • 快速排序对数组的分组类似于二叉树,我们可以简易的把快速排序的层数(递归深度)设为h(类比二叉树深度),递归创建的总函数栈帧次数设为N(类比二叉树节点个数)。
  • 则存在近似关系:\log{_{2}}N = h ,则共有 log{_{2}}N 层,每一层的每一个节点都要遍历数组,整个一层加起来的遍历次数近似N,则总遍历次数为log{_{2}}N * N = Nlog{_{2}}N
  • 则快速排序的时间复杂度为log{_{2}}N * N = Nlog{_{2}}N

4.2 空间复杂度

  • 快速排序所占用的额外空间主要为递归创建的函数栈帧,则空间复杂度就是递归创建的最大栈帧数量。由于栈的空间可以重复利用,则计算递归的最大深度即可,最大深度为:log{_{2}}N
  • 快速排序的空间复杂度为:log{_{2}}N

五、快速排序的优化

  • 快速排序在最好情况下可以看作一个完全二叉树,时间复杂度为Nlog{_{2}}N。但是如果排序数组有序,那么每次把数组首位置作为基准位置的话,每次排序就相当于将数组分为 1 和 N - 1 个元素。每次排好一个元素,那么递归的深度就会大大增加。

  • 遍历的总数就会变成一个等差数列,n + n - 1 + n - 2 + ... + 2 + 1,用求和公式求出结果后,最大的次方项变成了N^{2},这不仅仅严重降低了效率,也有可能会因为递归层数太深造成栈溢出的风险。
  • 为了解决这两个问题,可以使用三数取中小区间优化来解决这些问题。

5.1 三数取中

  • 三数取中的思想是,取数组首、末、中三个位置的值,记录这三个位置上大小为中间值的下标。并将这个取中的值与数组首元素交换。这样一来,以首尾值为基准值,基准值最终排好的位置会趋近于数组中间,就会将数组尽可能分为长度大致相等的两部分进行递归,以增加效率减少递归深度
//三数取中
int FindMid(int* a, int left, int right)
{int mid = (left + right) >> 1;if (a[left] < a[mid]){if (a[mid] < a[right])return mid;else //a[mid] >= a[right] mid最大,选left和right中最大的{if (a[left] > a[right])return left;elsereturn right;}}else //a[left] >= a[mid]{if (a[mid] > a[right])return mid;else //a[mid] <= a[right] mid最小,选left和right中最小的{if (a[right] < a[left])return right;elsereturn left;}}
}
  • 加入了三数取中,快速排序的核心代码就变成了:
void QSort(int* a, int left, int right)
{if (left >= right)return;int begin = left, end = right;int middle = FindMid(a, left, right);Swap(&a[left], &a[middle]);//交换int mid = Part3(a, begin, end);QSort(a, begin, mid - 1);QSort(a, mid + 1, end);
}

5.2 小区间优化

  • 小区间优化主要是针对排序数组的元素数量较少时,进行递归开辟函数栈帧开销太大(就是没有必要),不如使用其他的排序算法(快一些,但不额外开辟空间)来进行排序。一般情况下,小区间优化使用的排序算法为插入排序
//插入排序
void InsertSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){int end = i;int tmp = a[end + 1];while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];end--;}elsebreak;}a[end + 1] = tmp;}
}
  • 快速排序的主要逻辑变为:
void QSort(int* a, int left, int right)
{if (left >= right)return;if (right - left + 1 < 10){InsertSort(a + left, right - left + 1);//插入排序}else{int begin = left, end = right;int middle = FindMid(a, left, right);Swap(&a[left], &a[middle]);//交换int mid = Part3(a, begin, end);QSort(a, begin, mid - 1);QSort(a, mid + 1, end);}
}
  • 这里需要注意,由于递归进行到一定深度时,数组区间元素个数较少的情况下([left, right]),排序的区间是整个数组的一小段,故插入排序传入的首地址需要传 a + left,排序元素数量需要传right - left + 1

相关文章:

【排序】快速排序详解

✨✨欢迎大家来到Celia的博客✨✨ &#x1f389;&#x1f389;创作不易&#xff0c;请点赞关注&#xff0c;多多支持哦&#x1f389;&#x1f389; 所属专栏&#xff1a;排序 个人主页&#xff1a;Celias blog~ 一、快速排序的思想 快速排序的核心思想是&#xff1a; 选定一个…...

贪心算法总结(2)

一、买卖股票的最佳时机 . - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int maxProfit(vector<int>& prices) {int miniINT_MAX;int ret0;for(int&price:prices){//遍历的时候&#xff0c;我们随时去更新最小的值&#xff0c;然后让每一位…...

弘景光电:技术实力与创新驱动并进

在光学镜头及摄像模组产品领域&#xff0c;广东弘景光电科技股份有限公司&#xff08;以下简称“弘景光电”&#xff09;无疑是一颗耀眼的明星。自成立以来&#xff0c;弘景光电凭借其强大的研发实力、卓越的产品性能、精密的制造工艺以及严格的质量管理体系&#xff0c;在光学…...

2024年7月23日~2024年7月29日周报

目录 一、前言 二、完成情况 2.1 一种具有边缘增强特点的医学图像分割网络 2.2 融合边缘增强注意力机制和 U-Net 网络的医学图像分割 2.3 遇到的困难 三、下周计划 一、前言 上周参加了一些师兄师姐的论文讨论会议&#xff0c;并完成了初稿。 本周继续修改论文&#xff0…...

M3U8流视频数据爬虫

M3U8流视频数据爬虫 HLS技术介绍 现在大部分视频客户端都采用HTTP Live Streaming&#xff08;HLS&#xff0c;Apple为了提高流播效率开发的技术&#xff09;&#xff0c;而不是直接播放MP4等视频文件。HLS技术的特点是将流媒体切分为若干【TS片段】&#xff08;比如几秒一段…...

保护您的数字财富:模块化沙箱在源代码防泄露中的突破

在数字化浪潮中&#xff0c;企业面临着前所未有的数据安全挑战。源代码、商业机密、客户数据……这些宝贵的数字资产一旦泄露&#xff0c;后果不堪设想。SDC沙盒防泄密系统&#xff0c;以其卓越的技术实力和创新的解决方案&#xff0c;为企业提供了一个坚不可摧的安全屏障。 核…...

FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析

一、引言 AVIOContext是FFmpeg&#xff08;本文演示用的FFmpeg源码版本为5.0.3&#xff09;中的字节流上下文结构体&#xff0c;用来管理输入输出数据。打开一个媒体文件的时候&#xff0c;需要先把数据从硬盘读到缓冲区&#xff0c;然后会用到AVIOContext中的如下成员&#x…...

如何使用 API 查看极狐GitLab 镜像仓库中的镜像?

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab &#xff1a;https://gitlab.cn/install?channelcontent&utm_sourcecsdn 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署…...

软件-vscode-plantUML-IDEA

文章目录 vscode基础命令 实操1. vscode实现springboot项目搭建 &#xff08;包括spring data jpa和sqlLite连接&#xff09; PlantUMLIDEA下载及安装Eval Reset插件配置修改IDEA创建项目的默认目录IDEA配置gitIDEA翻译插件translationIDEA断点调试IDEA全局搜索快捷键不能使用代…...

ES6语法详解,面试必会,通俗易懂版

目录 Set的基本使用WeakSet 使用Set 和 WeakSet 区别内存泄漏示例&#xff1a;使用普通 Set 保存 DOM 节点如何避免这个内存泄漏MapWeakMap 的使用 Set的基本使用 在ES6之前&#xff0c;我们存储数据的结构主要有两种&#xff1a;数组、对象。 在ES6中新增了另外两种数据结构&a…...

CTFshow--Web--代码审计

目录 web301 web302 web303 web304 web305 web306 web307 web308 web309 web310 web301 开始一个登录框, 下意识sql尝试一下 发现 1 的时候会到一个 checklogin.php 的路径下, 但啥也没有 好吧, 这是要审计代码的 ,下载好源码, 开始审计 看了一下源码 , 应该就是sql…...

Java语言程序设计——篇十(1)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 接口介绍 接口概述接口定义接口的实现实战演练 &#x1f445;接口的继承实战演练实战演练 接口的类型常量实战演练 静态方法默认方法解决默认方…...

Qt对比MFC优势

从Qt小白到现在使用了有四年的时间&#xff0c;之前也搞过MFC,WinForm,基本上都是桌面的框架&#xff0c; 从难易程度看MFC>QT>WinForm; 运行的效率上来看MFC>QT>WinForm; 开发效率上WinForm>QT>MFC; 跨平台Qt首选&#xff1b; 界面的美观难易程度Qt>…...

RuntimeError: No CUDA GPUs are available

RuntimeError: No CUDA GPUs are available 目录 RuntimeError: No CUDA GPUs are available 【常见模块错误】 【解决方案】 解决步骤如下&#xff1a; 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科…...

URL参数中携带中文?分享 1 段优质 JS 代码片段!

本内容首发于工粽号&#xff1a;程序员大澈&#xff0c;每日分享一段优质代码片段&#xff0c;欢迎关注和投稿&#xff01; 大家好&#xff0c;我是大澈&#xff01; 本文约 800 字&#xff0c;整篇阅读约需 1 分钟。 今天分享一段优质 JS 代码片段&#xff0c;在发送 ajax 请…...

sass的使用

一、变量 //声明一个变量 $highlight-color: #F90; .selected {border: 1px solid $highlight-color; }//编译后 .selected {border: 1px solid #F90; }二、导入 import "xxx.scss"三、混合器简单定义 通过mixin定义&#xff0c;通过include调用 // mixin.scss /…...

【足球走地软件】走地数据分析预测【大模型篇】走地预测软件实战分享

了解什么是走地数据&#xff1f; 走地数据分析&#xff0c;在足球赛事的上下文中&#xff0c;是一种针对正在进行中的比赛进行实时数据分析的方法。这种方法主要用于预测比赛中的某些结果或趋势&#xff0c;如总进球数、比分变化、球队表现等。 在足球走地数据分析中&#xf…...

现在有什么赛道可以干到退休?

最近&#xff0c;一则“90后无论男女都得65岁以后退休”的消息在多个网络平台流传&#xff0c;也不知道是真是假&#xff0c;好巧不巧今天刷热点的时候又看到一条这样的热点&#xff1a;现在有什么赛道可以干到退休&#xff1f; 点进去看了几条热评&#xff0c;第一条热评说的…...

c程序杂谈系列(职责链模式与if_else)

从处理器的角度来说&#xff0c;条件分支会导致指令流水线的中断&#xff0c;所以控制语句需要严格保存状态&#xff0c;因为处理器是很难直接进行逻辑判断的&#xff0c;有可能它会执行一段时间&#xff0c;发现出错后再返回&#xff0c;也有可能通过延时等手段完成控制流的正…...

前端开发技术之CSS(层叠样式表)

盒模型&#xff08;Box Model&#xff09; CSS盒模型描述了如何计算一个元素的总宽度和高度。 它包括以下几个部分&#xff1a; 1. 内容&#xff08;Content&#xff09;&#xff1a;元素的实际内容&#xff0c;比如文本或图片。 2. 内边距&#xff08;Padding&#xff09;&…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量&#xff0c;招商蛇口以“美好生活承载者”为使命&#xff0c;深耕全球111座城市&#xff0c;以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子&#xff0c;招商蛇口始终与城市发展同频共振&#xff0c;以建筑诠释对土地与生活的…...