快速排序(下)
快速排序(下)
前言
在上一篇文章中我们了解了快速排序算法,但那是Hoare的版本,其实还有别的版本:一种是挖坑法,它们的区别主要在于如何找基准值。霍尔的版本思路难理解但代码好理解,挖坑法则是思路好理解但代码不好理解;还有一种是lomuto的前后指针法。
此外,还有不使用递归的快排方法(找基准值还是用的三种方法之一)。
本文就来讲解这几种不同的快排方法。
正文
挖坑法
实现思路:
创建左右指针。首先从右向左找出比基准值小的数据,找到后立即放入左边坑中,当前位置变为新的“坑”,然后从左向右找出比基准值大的数据,找到后立即放入右边坑中,当前位置变为新的“坑”,结束循环后将最开始存储的分界值放入当前的“坑”中,返回当前“坑”的下标(即分界值下标)。
画图理解一下什么是“坑”。
这就是整个“挖坑”然后“填坑”直到left和right重叠时将基准值放到该位置(它该待的位置)的过程。
现在我们可以来写一下挖坑法的代码:
//挖坑法
int _QuickSort(int* arr, int left, int right)
{int hole = left;int key = arr[hole];while (left < right)//注意和霍尔版区别{//这里同样需要限制且不能是arr[right] >= key,否则可能无法“二分”,最终效率低下while (left < right && arr[right] > key){--right;}arr[hole] = arr[right];hole = right;while (left < right && arr[left] < key){++left;}arr[hole] = arr[left];hole = left;}arr[hole] = key;return hole;
}
排序效果
我们在main函数中写这样的代码:
int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };int n = sizeof(a) / sizeof(int);printf("排序前: ");PrintArr(a, n);//代码不具体展示QuickSort(a, 0, n-1);printf("排序后: ");PrintArr(a, n);
执行结果:
可以看到我们就成功排序了。
lomuto前后指针法
实现思路:
创建前后指针,从左往右找比基准值小的进行交换,使得小的都排在基准值的左边。
其实前后指针对于很多学过数据结构的人来说应该已经不陌生了,但是快排也能用到前后指针法,我们看看具体怎么做:
- 定义两个变量prev和cur,让cur指向位置的数据和key值比较
- 若arr[cur]<arr[key],prev向后走一步并和cur交换
- 若arr[cur]>=arr[key],cur继续往后
- 退出循环后将prev与key值交换,找到基准值
同样不变的思路也是要找基准值,也就是要让基准值左侧的数据都小于基准值,让基准值右侧的数据都大于基准值。
那么两个变量的作用是什么?可以说prev是用来占坑的,而cur用来找小,找到小后就让prev加一后和cur交换。退出循环后将prev与key值交换,就找到了基准值。
代码:
//lomuto前后指针法
int _QuickSort(int* arr, int left, int right)
{int prev = left, cur = left + 1;int key = left;while (cur<=right){if (arr[cur] < arr[key] && ++prev !=cur)//prev和cur相同就不交换//写为arr[cur] <= arr[key],加上等号也是殊途同归{Swap(&arr[cur], &arr[prev]);}cur++;//进不进循环cur都要往后走}Swap(&arr[key], &arr[prev]);return prev;
}
执行效果:
也一样成功排序完了。
可以发现这一种方法比起前面的Hoare版本和挖坑法,在取不取等号的探讨上少了很多麻烦。
-
注意:
在循环内的if语句中arr[cur] < arr[key]如果写为arr[cur] <= arr[key],并不能解决无法”二分“子序列的问题,所以加不加都无所谓。
这也就是前后指针法的一个**缺陷:如果数组中数据都相等**,效率就会很低。
快排的非递归版本
所以现在已经知道了三种版本的快排,其实也就是三种找基准值的方法。
既然我们的快排使用的是递归,就难免有空间上的缺陷。其实快排还有非递归版本。但是要借助数据结构:栈。
那么现在问题是左右区间再分左右区间时我们怎么找区间呢?因为找基准值时没有使用递归而是在找区间时使用的递归(所以找基准值使用前面说的三种方法的任意一种都行)。
(先将5和0入栈,然后出栈,left=0,right=5,找到基准值为3)
我们知道keyi(基准值)也就能区分左区间和右区间,这两个区间任意哪一个入栈都行。
现在,我们还是一样要现将right入栈再将left入栈(先入右区间),然后再入另一个区间。
此时栈不为空,我们出栈,先出的两个就是左区间,然后去找它的基准值。
然后这样找下去直到right小于left或等于left时,也就是没有数据或者只有一个数据时,构不成有效区间,就不入栈。
左区间所有基准值找完后,就像二叉树一样,开始走右区间。
开始走右区间时也就是刚好栈里剩的两个数据为右区间时。
所以可以看出,我们取栈顶元素就是在模拟递归。
代码参考
现在我们通过代码来看看怎么使用栈进行快排。
//非递归版快排
//借助数据结构——栈
void QuickSortNonR(int* arr, int left, int right)
{ST st;STInit(&st);StackPush(&st, right);StackPush(&st, left);while (!StackEmpty(&st)){//取两次栈顶int begin = StackTop(&st);StackPop(&st);int end = StackTop(&st);StackPop(&st);//找基准值——用双指针法int prev = begin;int cur = begin + 1;int keyi = begin;while (cur <= end){if (arr[cur] <= arr[keyi] && ++prev!=cur){Swap(&arr[cur], &arr[prev]); }cur++;}Swap(&arr[keyi], &arr[prev]);keyi = prev;//根据基准值划分左右区间//左区间:[begin,keyi-1]//右区间:[keyi+1,end]if (keyi + 1 < end)//控制区间有效{StackPush(&st, end);StackPush(&st, keyi + 1);} if (keyi-1>begin)//控制区间有效{StackPush(&st, keyi - 1);StackPush(&st, begin);}}STDestroy(&st);
}
那么到此本文就结束了,祝学习愉快=_=
相关文章:

快速排序(下)
快速排序(下) 前言 在上一篇文章中我们了解了快速排序算法,但那是Hoare的版本,其实还有别的版本:一种是挖坑法,它们的区别主要在于如何找基准值。霍尔的版本思路难理解但代码好理解,挖坑法则是…...

LazyLLM:长上下文场景下提高LLM推理效率
LazyLLM旨在优化大型语言模型(LLM)在处理长文本语境下的推理效率。传统上,LLM的推理过程分为预填充和解码两个阶段,其中预填充阶段负责计算并存储输入提示的所有token的键值(KV)缓存,这一步骤在…...

PDF文件点击打印无反应?是何原因造成能解决吗?
PDF无法打印怎么处理?在我们工作中,经常会遇见各种各样的文件问题,当我们想要将PDF文件打印出来纸质版使用,却不知什么原因,显示PDF无法打印,这时应该怎么处理呢? 一般情况下,PDF文件…...

初学者友好!从零到一快速上手PyCharm安装的超详细图解+避坑指南教程
一,pycharm的官网下载 下载地址:www.jetbrains.com/pycharm/ 本文将从 Python解释器安装到Pycharm专业版安装和配置汉化等使用都进行了详细介绍,希望能够帮助到大家。 Python解释器&Pycharm安装包&Pycharm破姐插件我都打包好了。 …...

AI大模型需要什么样的数据?
数据将是未来AI大模型竞争的关键要素 人工智能发展的突破得益于高质量数据的发展。例如,大型语言模型的最新进展依赖于更高质量、更丰富的训练数据集:与GPT-2相比,GPT-3对模型架构只进行了微小的修改,但花费精力收集更大的高质量…...

Java每日一练_模拟面试题1(死锁)
一、死锁的条件 死锁通常发生在两个或者更多的线程相互等待对方释放资源,从而导致它们都无法继续执行。死锁的条件通常被描述为四个必要条件,也就是互斥条件、不可剥夺条件、占有并等待条件和循环等待条件。 互斥条件:资源不能被共享&#x…...

第三方库认识- Mysql 数据库 API 认识
文章目录 一、msyql数据库API接口1.初始化mysql_init()——mysql_init2.链接数据库mysql_real_connect——mysql_real_connect3.设置当前客户端的字符集——mysql_set_character_set4.选择操作的数据库——mysql_select_db5.执行sql语句——mysql_query6.保存查询结果到本地——…...

Python兼职接单全攻略:掌握技能,拓宽收入渠道
引言 随着Python在数据处理、Web开发、自动化办公、爬虫技术等多个领域的广泛应用,越来越多的人开始利用Python技能进行兼职接单,以此拓宽收入渠道。本文将详细介绍Python兼职接单的注意事项、所需技能水平、常见单子类型、接单途径及平台,帮…...

一键编译并启动一个 ARM Linux qemu 虚拟机
需要事先自己编译 qemu-system-arm 可执行文件; 1,编译创建ARM 虚拟机 #!/usr/bin/bash sudo lssudo apt-get install gcc-arm-linux-gnueabi#wget https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.10.tar.gztar zxf linux-kernel-v5.10…...

KubeVirt虚拟机存储及网络卸载加速解决方案
1. 方案背景 1.1. KubeVirt介绍 随着云计算和容器技术的飞速发展,Kubernetes已成为业界公认的容器编排标准,为用户提供了强大、灵活且可扩展的平台来部署和管理各类应用。然而,在企业的实际应用中,仍有许多传统应用或遗留系统难…...

JVM—对象已死?
参考资料:深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)周志明 在堆里面存放着 Java 世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象之中哪些还“存活”着,哪些已经“死去”。 1、如何判…...

【前端面试3+1】20 css三栏布局6种实现方式、多行文本溢出怎么实现、token过期了怎么处理、【二叉树的中序遍历】
一、css三栏布局6种实现方式 1.浮动布局(Floats) .container {overflow: auto; /* 清除浮动 */ }.left, .right {width: 20%; /* 左右栏宽度 */float: left; }.middle {width: 60%; /* 中间栏宽度 */margin: 0 20%; /* 左右栏宽度 */ } 2.Flexbox .conta…...
【C++】vector介绍以及模拟实现(超级详细<=>源码并存)
欢迎来到我的Blog,点击关注哦💕 【C】vector介绍以及模拟实现 前言vector介绍 vector常见操作构造函数iteratorcapacitymodify vector模拟实现存储结构默认构造函数构造函数拷贝构造函数赋值运算符重载析构函数 容量(capacity)si…...

【Redis 进阶】主从复制(重点理解流程和原理)
在分布式系统中为了解决单点问题(某个服务器程序只有一个节点(只搞一个物理服务器来部署这个服务器程序)。可用性不高:如果这个机器挂了意味着服务就中断了;性能 / 支持的并发量比较有限)。通常会把数据复制…...

Git常用命
转自:https://blog.csdn.net/ahjxhy2010/article/details/80047553 1.查看某个文件或目录的修改历史 git log filename #查看fileName相关的commit记录 git log -p filenam # 显示每次提交的diff#只看某次提交中的某个文件变化,commit-id 文件名…...
强化学习时序差分算法之Q-learning算法——以悬崖漫步环境为例
0.简介 基于时序差分算法的强化学习算法除了Sarsa算法以外还有一种著名算法为Q-learning算法,为离线策略算法,与在线策略算法Sarsa算法相比,其时序差分更新方式变为 Q(St,At)←Q(St,At)α[Rt1γmaxaQ(St1,a)−Q(St,At)] 对于 Sarsa 来说&am…...
111推流111
推流推流...
刷题——数组中只出现一次的两个数字
数组中只出现一次的两个数字_牛客题霸_牛客网 描述 一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。 数据范围:数组长度 2≤n≤10002≤n≤1000,数组中每个数的大小 0<val≤100000…...
《剖析程序员面试“八股文”:助力、阻力还是噱头?》
#“八股文”在实际工作中是助力、阻力还是空谈? 作为现在各类大中小企业面试程序员时的必问内容,“八股文”似乎是很重要的存在。但“八股文”是否能在实际工作中发挥它“敲门砖”应有的作用呢?有IT人士不禁发出疑问:程序员面试考…...
Redis过期key的删除策略
在 Redis 中,设置了过期时间的键在过期时间到达后,并不会立即从内存中删除。如果不是,那过期后到底什么时候被删除呢? 下面对这三种删除策略进行具体分析。 立即删除: 立即删除能够保证内存数据的及时性和空间的有效…...

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...

ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...

面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...