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

c语言快速排序(霍尔法、挖坑法、双指针法)图文详解

快速排序介绍:

  快速排序是一种非常常用的排序方法,它在1962由C. A. R. Hoare(霍尔)提的一种二叉树结构的交换排序方法,故因此它又被称为霍尔划分,它基于分治的思想,所以整体思路是递归进行的。

整体思路:

1.先选取一个key,关于key值的选取,一般是选数组第一个元素,数组中间元素,数组最后一个元素,这三个元素的中间值,并将这个元素与数组第一个元素进行交换。

2.将key放入整个区间中正确的位置,即为key左边的元素都比key小,右边的元素都比key要大,此时的key就是它排好序的位置,注意key左边的元素都比它小,但不一定有序,右边也是一样,然后根据递归的思想,再对key左边的区间进行上面一样的操作和key右边的区间进行上面一样的的操作,当区间不存在或者区间只有一个元素时返回。

如何将key放入正确的位置:

  将key放入正确的位置正是每趟递归需要做的,那么具体该如何实现呢?

  实现过程目前有三种方法,每种方法虽然写法不同,但总体思路一样,所以效率是相同的,只要完全理解快速排序,写哪种都一样。

1.霍尔版本(传统方法)

第一步:定义一个right从数组最后一个元素开始即数组的右边开始向左边遍历,如果找到比key小的值就停下来。

第二步:定义一个left从数组第一个元素开始即数组的左边开始向右遍历,如果找到比key大的值就停下来。

第三步:left和right都停下来之后,交换left和right的值,这一步的目的就是将比key小的值往左放,将比key大的值。

第四步:当left和right相遇后,将第一个元素(即为key的值)与它们相遇位置的值交换。

第五步:让他们相遇位置的左区间和右区间同样执行上述四步(即为递归)。

动态思路图:

  

代码实现:

void Swap(int* a, int* b)
{int tmp = 0;tmp = *a;*a = *b;*b = tmp;
}int GetMidi(int* a, int begin, int end)
{int midi = (begin + end) / 2;if (a[begin] > a[midi]){if (a[midi] > a[end]){return midi;}else if (a[end] > a[begin]){return begin;}else{return end;}}else{if (a[begin] > a[end]){return begin;}else if (a[end] > a[midi]){return midi;}else{return end;}}
}void QuickSortHoare(int* a, int begin, int end)
{int left = begin;int right = end;if (left >= right){return;}int midi = GetMidi(a, begin, end);Swap(&a[begin], &a[midi]);int keyi = begin;while (left < right){while (left < right && a[right] >= a[keyi]){right--;}while (left < right && a[left] < a[keyi]){left++;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);keyi = left;QuickSortHoare(a, begin, keyi - 1);QuickSortHoare(a, keyi + 1, end);
}

2.挖坑法:

第一步:将key的位置(即为第一个元素的位置)作为第一个坑位,将key的值一直保存在变量key中。

第二步:定义一个right从数组最后一个元素开始即为数组右边开始向左遍历,如果找到比key小的值,right停下来,将right下标访问的元素赋值到上一个坑位,并将right作为新的坑位。

第三步:定义一个left从数组第一个元素开始即为数组左边开始向右遍历,如果找到比key大的值,left停下来,将left下标访问的元素赋值到上一个坑位,并将left作为新的坑位。

第四步:当right和left相遇时,此时它们访问的元素绝对是坑位,只需将key里保存的key值放入坑位即可。

第五步:让他们相遇位置的左区间和右区间同样执行上述四步(即为递归)。

思路图:

代码实现:

void Swap(int* a, int* b)
{int tmp = 0;tmp = *a;*a = *b;*b = tmp;
}int GetMidi(int* a, int begin, int end)
{int midi = (begin + end) / 2;if (a[begin] > a[midi]){if (a[midi] > a[end]){return midi;}else if (a[end] > a[begin]){return begin;}else{return end;}}else{if (a[begin] > a[end]){return begin;}else if (a[end] > a[midi]){return midi;}else{return end;}}
}
void QuickSortHole(int* a, int begin, int end)
{int left = begin;int right = end;if (begin >= end){return;}int midi = GetMidi(a, begin, end);Swap(&a[begin], &a[midi]);int key = a[begin];int hole = begin;while (left < right){while (left < right && a[right] >= key){right--;}a[hole] = a[right];hole = right;while (left < right && a[left] <= key){left++;}a[hole] = a[left];hole = left;}a[hole] = key;int keyi = hole;QuickSortHole(a, begin, keyi - 1);QuickSortHole(a, keyi + 1, end);
}

3.双指针法(新手推荐)

第一步:定义两根指针cur和prev,初始位置如下图所示:

第二步:cur开始往后走,如果遇到比key小的值,则++prev,然后交换prev和cur指向的元素,再++cur,如果遇到比key大的值,则只++cur。

第三步:当cur访问过最后一个元素后,将key的元素与prve访问的元素交换位置。cur访问完整个数组后的各元素位置如下图所示:

第四步:让prev的左区间和右区间同样执行上述三步(即为递归)。

代码实现:

void Swap(int* a, int* b)
{int tmp = 0;tmp = *a;*a = *b;*b = tmp;
}int GetMidi(int* a, int begin, int end)
{int midi = (begin + end) / 2;if (a[begin] > a[midi]){if (a[midi] > a[end]){return midi;}else if (a[end] > a[begin]){return begin;}else{return end;}}else{if (a[begin] > a[end]){return begin;}else if (a[end] > a[midi]){return midi;}else{return end;}}
}void QuickSortD(int* a, int begin, int end)
{if (begin >= end){return;}int midi = GetMidi(a, begin, end);Swap(&a[begin], &a[midi]);int keyi = begin;int prev = begin;int cur = begin + 1;while (cur <= end){if (a[cur] < a[keyi] && ++prev != cur){Swap(&a[cur], &a[prev]);}cur++;}Swap(&a[keyi], &a[prev]);keyi = prev;QuickSortD(a, begin, keyi - 1);QuickSortD(a, keyi + 1, end);
}

下期预告:非递归

  这期讲的三种快速排序方法均是采用递归的方法来实现的,那么如何使用非递归来实现快速排序呢?下期我将发布快速排序的非递归法。

相关文章:

c语言快速排序(霍尔法、挖坑法、双指针法)图文详解

快速排序介绍&#xff1a; 快速排序是一种非常常用的排序方法&#xff0c;它在1962由C. A. R. Hoare&#xff08;霍尔&#xff09;提的一种二叉树结构的交换排序方法&#xff0c;故因此它又被称为霍尔划分&#xff0c;它基于分治的思想&#xff0c;所以整体思路是递归进行的。 …...

【mysql】锁的类型有哪些呢?

0 回答 根据数据的访问级别来区分&#xff1a; mysql锁分为共享锁和排他锁&#xff0c;也叫做读锁和写锁。读锁是共享的&#xff0c;可以通过lock in share mode实现&#xff0c;这时候只能读不能写。写锁是排他的&#xff0c;它会阻塞其他的写锁和读锁。 从颗粒度来区分&am…...

uniapp 显示文件流图片

如果是需要将文件流保存到相册&#xff0c;可以先转base64.详情见>uniapp app将base64保存到相册,uniapp app将文件流保存到相册-CSDN博客 uni.request({url: "www.baidu.com",data: {},header: {content-type:application/json,Authorization: "token"…...

多线程------ThreadLocal详解

目录 1. 什么是 ThreadLocal&#xff1f; 2. 如何使用 ThreadLocal&#xff1f; 3. ThreadLocal 的作用 4. ThreadLocal 的应用场景 5. ThreadLocal 的注意事项 我的其他博客 ThreadLocal 是 Java 中一个很有用的类&#xff0c;它提供了线程局部变量的支持。线程局部变量…...

【C++】POCO学习总结(十六):随机数、密码、时间戳、日期和时间(格式化与解析)、时区、本地时间

【C】郭老二博文之&#xff1a;C目录 1、Poco::Random 随机数 1.1 说明 POCO包括一个伪随机数生成器(PRNG)&#xff0c;使用非线性加性反馈算法&#xff0c;具有256位状态信息和长达269的周期。 PRNG可以生成31位的伪随机数。 它可以生成UInt32, char, bool, float和double…...

打补丁,生成.diff文件

作者&#xff1a;爱塔居 文章目录 目录 前言 步骤 一、在根目录上&#xff0c;输入添加指令 二、输入修改内容指令 三、生成补丁 前言 自己的理解&#xff0c;仅供参考&#xff0c;欢迎指正。 补丁的话&#xff0c;在我看来就是方便评审&#xff0c;更方便看修改代码吧。 步骤…...

《LeetCode力扣练习》代码随想录——字符串(KMP算法学习补充——针对next数组构建的回退步骤进行解释)

《LeetCode力扣练习》代码随想录——字符串&#xff08;KMP算法学习补充——针对next数组构建的回退步骤进行解释&#xff09; 学习路径 代码随想录&#xff1a;28. 实现 strStr() CSDN&#xff1a;【详解】KMP算法——多图&#xff0c;多例子&#xff08;c语言&#xff09; …...

【CANoe】CAPL中on signal和on signal_update的区别

文章目录 CAN信号事件 CAN信号事件 CAN信号事件是在CAN总线上出现指定的信号时被调用&#xff08;需要配合DBC文件使用&#xff09;。 关键字为&#xff1a;on signal xxx或on signal_update xxx。 on signal xxx:只在指定信号的值发生变化时被调用&#xff0c; on signal_u…...

ArrayList集合的两个实例应用,有趣的洗牌算法与杨辉三角

本节课的内容&#xff0c;就让我们来学习一下ArrayList集合的应用&#xff0c;ArrayList的本质就是一个顺序表&#xff0c;那下面一起来学习吧 目录 一、杨辉三角 1.题目详情及链接 2.剖析题目 3.思路及代码 二、洗牌算法 1.创造牌对象 2.创造一副牌 3.洗牌操作 4.发…...

Qt 剪贴板操作

Qt剪贴板操作 剪贴板的操作经常和前面所说的拖放技术在一起使用,因此我们现在先来说说剪贴板的相关操作。大家对剪贴板都很熟悉。我们可以简单的把它理解成一个数据的存储池,可以把外面的数据放置进去,也可以把里面的数据取出来。剪贴板是由操作系统维护的,所以这提供了跨…...

python 学习笔记20 批量修改页眉页脚

需求&#xff1a;修改指定目录下所有文件的页眉页脚&#xff0c;或者往里面添加内容。 1. 这里做了word的实现和excel的实现&#xff0c;如下&#xff1a; 需要先安装 pip3 install pywin32&#xff0c;另外页眉页脚格式设置可以参考&#xff1a; word&#xff1a; 浅谈Wor…...

IIS + Axios 跨域设置

1、服务器端设置IIS &#xff08;web.config) 即可&#xff0c;不需要对django settings.py做配置&#xff08;python manage.py runserver 才需要settings.py配置跨域&#xff0c;IIS在iis上配&#xff09; 网站根目录的web.config中加上这段&#xff1a; <httpProtocol&…...

详细说说vuex

Vuex 是什么 Vuex有几个属性及作用注意事项vuex 使用举例Vuex3和Vuex4有哪些区别 创建 Store 的方式在组件中使用 Store辅助函数的用法响应式的改进Vuex4 支持多例模式 Vuex 是什么 Vuex是一个专门为Vue.js应用设计的状态管理构架&#xff0c;它统一管理和维护各个Vue组件的可…...

Qt之Ui样式表不影响子类的配置

Qt之Ui样式表不影响子类的配置 问题 在ui界面上布局时&#xff0c;当对容器进行样试设计时&#xff0c;会对容器内其它成员对象也进行了修改 分析 对应*.ui文件内容 从这个写法来看&#xff0c;它的样式属性会影响其成员对象样式属性。 解决方法 在容器的样式表中写时适…...

Java集合--Map

1、Map集合概述 在Java的集合框架中&#xff0c;Map为双列集合&#xff0c;在Map中的元素是成对以<K,V>键值对的形式存在的&#xff0c;通过键可以找对所对应的值。Map接口有许多的实现类&#xff0c;各自都具有不同的性能和用途。常用的Map接口实现类有HashMap、Hashtab…...

C语言—每日选择题—Day48

第一题 1. 已知宏定义&#xff1a; #define M y*y3*y &#xff0c; 则表达式 s3*M4*My*M 预处理阶段后的结果是 A&#xff1a;s3*(y*y3*y)4*(y*y3*y)y*(y*y3*y) B&#xff1a;s3*(y*y)3*y4*(y*y)3*yy*(y*y)3*y C&#xff1a;s3*y*y3*y4*y*y3*yy*y*y3*y D&#xff1a;s3*(y*y)(3…...

华为OD试题七(IPv4地址转换成整数、比赛的冠亚季军)

1. IPv4地址转换成整数 示例代码&#xff1a; #测试数据 s1 "100#101#1#5"def fun(s):s_list s.split("#")# 转化成十六进制数 左边补零s_16_list [hex(int(_))[2:].zfill(2) for _ in s_list]s_16_str .join(s_16_list)return int(s_16_str,16) r f…...

SVN优缺点详解及版本控制系统选型建议

Subversion (SVN)是目前可用的众多版本控制选项之一。本篇文章将全面概述什么是 SVN、SVN的历史、SVN存储库是什么&#xff0c;以及在切换到SVN之前您应该谨慎考虑的潜在问题。 什么是Subversion&#xff08;SVN&#xff09;&#xff1f; Subversion软件&#xff0c;也称为SV…...

自己动手写数据库: select 查询语句对应查询树的构造和执行

首先我们需要给原来代码打个补丁&#xff0c;在SelectScan 结构体初始化时需要传入 UpdateScan 接口对象&#xff0c;但很多时候我们需要传入的是 Scan 对象&#xff0c;因此我们需要做一个转换&#xff0c;也就是当初始化 SelectScan 时&#xff0c;如果传入的是 Scan 对象&am…...

扬声器(喇叭)

扬声器(喇叭) 电子元器件百科 文章目录 扬声器(喇叭)前言一、扬声器(喇叭)是什么二、扬声器(喇叭)的类别三、扬声器(喇叭)的应用场景四、扬声器(喇叭)的作用原理总结前言 扬声器广泛应用于音响系统、公共广播系统、汽车音响、电视、电脑和移动设备等各种电子设备…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...