120.【C语言】数据结构之快速排序(详解Hoare排序算法)
目录
1.Hoare单趟排序思想
2.单趟排序代码
初次写的代码
运行结果
查找问题原因
尝试解决问题
初次修正后代码
运行结果
正确的单趟排序代码
3.将单趟排序嵌入
如何递归?
递归结束的条件
1.较容易分析的结束条件:left==right
2.以{2,1}为例分析另一个结束条件
完整代码
运行结果
1.Hoare单趟排序思想
例如对于无序数组arr={6,1,2,7,9,3,4,5,10,8},排升序
① 选一个基准值(又叫关键值)key,一般来说key选arr[0](其实key也可以取其他值,下篇会详细介绍)
② 如果数组一共n个数,两个指针left和right分别从数组的左侧arr[0]和右侧arr[n-1]出发,其中right先走
③ left找到arr[left]比key小的就停止right需要找到arr[right]比key大的,之后arr[left]和arr[right]交换,left和right继续寻找,直到left>right或left==right退出
④ 最后将arr[key_i]和arr[left]交换,完成一次单趟排序
{6,1,2,7,9,3,4,5,10,8} --> {3,1,2,5,4,6,9,7,10,8}(比6小的都在6的左边.比6大的都在6的右边)
2.单趟排序代码
初次写的代码
//单趟快速排序
int key_i = left;
while (left < right)
{//由于key_i==left,因此right指针先走//右边找小while (arr[right] > arr[key_i]){right--;}//左边找大while (arr[left] < arr[key_i]){left++;}Swap(&arr[left], &arr[right]);
}Swap(&arr[key_i], &arr[left]);
运行结果
实际上运行结果不正确,6左侧有9,9比6大
查找问题原因
可以在while (arr[right] > arr[key_i])处下断点调试
发现问题:left==0不符合要求
尝试解决问题
可在循环前让left==1跳过arr[0]
int key_i = left;left++;while (left < right){//......}
执行结果:实际上运行结果不正确,6左侧有9,9比6大
尝试寻找原因
下断点调试,
上图说明:到目前为止都没有什么问题
执行到下图时,问题出现:left>right还没有退出!
因此要在两个内循环中都加入条件: left<right,确保不越界!
初次修正后代码
//单趟快速排序
int key_i = left;
left++;
while (left < right)
{//由于key_i==left,因此right指针先走//右边找小while (left < right && arr[right] > arr[key_i]){right--;}//左边找大while (left < right && arr[left] < arr[key_i]){left++;}Swap(&arr[left], &arr[right]);
}Swap(&arr[key_i], &arr[left]);
运行结果
没有问题了,与设想的结果相同
其实修正后的代码还有巨大的问题!!
对{1,2,3,4,5}排序,执行结果反而将升序数组的顺序打乱了,问题在哪里?
执行到Swap(&arr[key_i], &arr[left]);时,key_i==0,left==1,导致错误互换,应该将left++删除
执行结果:没有问题了,与设想的结果相同
其实修正后的代码还有巨大的问题!!
对这个数组排序{6,1,2,7,9,3,4,5,10,6}会死循环
arr[right] == arr[key_i] 和 arr[left] == arr[key_i]时right没有变化,导致一直在while (left<right)里执行无法退出
改成while (left < right && arr[right] >= arr[key_i])和while (left < right && arr[left] <= arr[key_i])即可让left和right指针继续查找,而且避免了在while (left<right)执行前让left++的情况
正确的单趟排序代码
//单趟快速排序int key_i = left;while (left < right){//由于key_i==left,因此right指针先走//右边找小while (left < right && arr[right] >= arr[key_i]){right--;}//左边找大while (left < right && arr[left] <= arr[key_i]){left++;}Swap(&arr[left], &arr[right]);}Swap(&arr[key_i], &arr[left]);
3.将单趟排序嵌入
如何递归?
第一次单趟排序后的数组{3,1,2,5,4,6,9,7,10,8},接下来分别对6的左侧数据和右侧数据排序
左侧数据表示的区间为[begin,key_i-1] ,右侧数据表示的区间为[key_i+1,end],直接递归调用即可
int begin = left;int end = right;//循环省略QuickSort(arr, begin, key_i - 1);QuickSort(arr, key_i + 1,end);
考虑到循环执行时,left和right的值会改变,因此需要先保存left和right的值至begin和end中
画图为(极像二叉树的递归)
递归结束的条件
何时从递推到回归?(注意返回的条件写在QuickSort函数的最前面)
1.较容易分析的结束条件:left==right
2.以{2,1}为例分析另一个结束条件
则另一个结束条件为left>right
综上:递推结束的条件为left>=right
(其实从区间也能看出递推结束的条件:左区间 [begin,key_i-1]推出当begin<key_i-1时区间成立,begin对应形参left,key_i-1对应形参right,因此left<right;右区间 [key_i+1,end]推出当key_i+1<end时区间成立,key_i+1对应形参left,end对应形参right,因此left<right)
**注意:递归函数先写递归的结束条件再写递推的具体内容**
完整代码
void QuickSort(int* arr, int left, int right)
{if (left >= right)return;//单趟快速排序int begin = left;int end = right;int key_i = left;while (left < right){//由于key_i==left,因此right指针先走//右边找小while (left < right && arr[right] >= arr[key_i]){right--;}//左边找大while (left < right && arr[left] <= arr[key_i]){left++;}Swap(&arr[left], &arr[right]);}Swap(&arr[key_i], &arr[left]);key_i = left;//arr[key_i]和arr[left]交换后下标要改变,否则会对下次递归产生不利结果QuickSort(arr, begin, key_i - 1);QuickSort(arr, key_i + 1,end);
}
运行结果
注:有关Hoare排序为什么key_i==left要让right先走的原因和Hoare排序的优化见下篇文章
相关文章:

120.【C语言】数据结构之快速排序(详解Hoare排序算法)
目录 1.Hoare单趟排序思想 2.单趟排序代码 初次写的代码 运行结果 查找问题原因 尝试解决问题 初次修正后代码 运行结果 正确的单趟排序代码 3.将单趟排序嵌入 如何递归? 递归结束的条件 1.较容易分析的结束条件:leftright 2.以{2,1}为例分析另一个结束条件 完整…...

uniapp通过v-if进行判断时,会出现闪屏?【已解决】
1.问题:按钮切换时,通过v-if来判断,会出现闪烁情况,影响用户体验 2.v-if 闪烁问题可能的原因 条件切换频繁:如果 v-if 指令的条件在短时间内频繁切换,会导致元素不断被销毁和重新创建,从而…...
各种网站(学习资源、常用工具及其他,持续更新中~)
欢迎围观笔者的个人博客~ 也欢迎通过RSS网址https://kangaroogao.github.io/atom.xml进行订阅~ 大学指南 上海交通大学生存手册中国科学技术大学人工智能与数据科学学院本科进阶指南USTC不完全入学指南大学生活质量指北科研论 信息搜集 AI信息搜集USTC飞跃网站计算机保研 技…...
网络技术-QoS策略以及如何定义 流分类,流行为,流策略
一:QoS策略简介 QoS策略由如下部分组成: 类,定义了对报文进行识别的规则。 流行为,定义了一组针对类识别后的报文所做的QoS动作。 通过将类和流行为关联起来,QoS策略可对符合分类规则的报文执行流行为中定义的…...
线程晨考day20
1.线程的五种状态 创建 就绪 运行 阻塞 死亡 2.创建线程的两种方式 继承Thread类 重写run方法 实现Runnable接口 重写run方法 3.调用start和调用run方法的区别 调用start方法表示会开启新的线程 run方法不会开启新的线程 4.线程调度常用的方法 sleep() join() yield() 5.进程和…...
【ES6复习笔记】迭代器(10)
什么是迭代器? 迭代器(Iterator)是一种对象,它能够遍历并访问一个集合中的元素。在 JavaScript 中,迭代器提供了一种统一的方式来处理各种集合,如数组、字符串、Map、Set 等。通过迭代器,我们可…...

MySQL的TIMESTAMP类型字段非空和默认值属性的影响
同事说他通过某款商业数据同步软件将一个 MySQL 5.7.28 的库同步到 MySQL 5.7.20 的库时,如果表中含有 TIMESTAMP 数据类型、缺省值为 current_timestamp 的字段,这些表的同步任务就都失败了,而另外的一些包含了 DATETIME 数据类型的表就同步…...

【Linux进程】初悉进程
学习编程就得循环渐进,扎实基础,勿在浮沙筑高台 循环渐进Forward-CSDN博客 进程调度简介 1.2进程查看命令 1.3进程的几个要素 二、进程的生命周期 2.1进程状态文字描述 2.2进程状态的切换 2.3task_struct数据结构 2.4进程优先级 ⑴优先级的代…...

Python学习之路(5)— 使用C扩展
Python学习之路(5)— 使用C扩展 一、前言 参考:https://www.cnblogs.com/yinguo/p/4641349.html Python C扩展是指用C语言编写的代码,然后编译成Python可以调用的库。这样可以提高Python代码的执行效率,或者实现某些…...

动态规划34:446. 等差数列划分 II - 子序列
动态规划解题步骤: 1.确定状态表示:dp[i]是什么 2.确定状态转移方程:dp[i]等于什么 3.初始化:确保状态转移方程不越界 4.确定填表顺序:根据状态转移方程即可确定填表顺序 5.确定返回值 题目链接:446.…...

PPT画图——如何设置导致图片为600dpi
winr,输入regedit打开注册表 按路径找,HKEY_CURRENT_USER\Software\Microsoft\Office\XX.0\PowerPoint\Options(xx为版本号,16.0 or 15.0或则其他)。名称命名:ExportBitmapResolution 保存即可,…...

【模块系列】STM321.69TFT屏幕
前言 在翻翻自己的器件盒的时候,发现这块好久之前买的TFT屏了,想起还没有用STM32点亮过,手头上正好有立创的梁山派STM32F4,就试着按照网上的文章教程顺便移植个LVGL看看,然后就有了就本文。 代码工程命名的是LvglDemo&…...

大模型辅助测试的正确打开方式?
测试的基本目的之一,是对被测对象进行质量评估。换言之,是要提供关于被测对象质量的“确定性”。因此,我们很忌讳在测试设计中引入“不确定性”,比如采用不可靠的测试工具、自动化测试代码逻辑复杂易错、测试选择假设过于主观等等…...
三相电的相电压、线电压、额定值、有效值,变比,零序电压,零序电流,三相三线制的三角形连接,三相四线制的星形连接
在二次设备配置中经常有根电压系统相关的名词,本身不是学电气的,有些名词经常查了忘,后续工作所有遇到跟电气相关的知识总结在此帖,便于后续直接查看,避免每次都要重新查、重新梳理。 相电压和线电压的关系是根号3倍&a…...
电商网站的基础用户数在100万,日活跃用户数在1万左右,系统下单TPS最大支持1000,应用服务要保证高可用。请预估该网站每天的使用成本。
要预估一个电商网站每天的使用成本,我们需要考虑多个因素,包括计算资源、数据库、缓存、存储、网络流量、负载均衡、安全服务、监控与日志等。以下是基于您提供的信息(基础用户数100万,日活跃用户数1万,系统下单TPS最大…...

线性代数期末总复习的点点滴滴(1)
一、可逆矩阵、行列式、秩的关系 1.行列式与可逆矩阵的关系 所以,不难看出矩阵可逆的充分必要条件是该矩阵的行列式不为0。 2.接着来看,满秩和矩阵行列式的关系 不难看出满秩和行列式不为0是等价的。 3.再来看,满秩和矩阵可逆的关系 说明了…...

python+reportlab创建PDF文件
目录 字体导入 画布写入 创建画布对象 写入文本内容 写入图片内容 新增页 画线 表格 保存 模板写入 创建模板对象 段落及样式 表格及样式 画框 图片 页眉页脚 添加图形 构建pdf文件 reportlab库支持创建包含文本、图像、图形和表格的复杂PDF文档。 安装&…...
2024最新qrcode.min.js生成二维码Demo
找了一堆代码一堆GPT,终于给写对了: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><…...

【Microi吾码】开源力量赋能低代码创新,重塑软件开发生态格局
我的个人主页 文章专栏:Microi吾码 一、引言 在当今数字化浪潮汹涌澎湃的时代,软件开发的需求呈现出爆发式增长。企业为了在激烈的市场竞争中脱颖而出,不断寻求创新的解决方案以加速数字化转型。传统的软件开发方式往往面临着开发周期长、技…...

Github - 如何提交一个带有“verified”标识的commit
Github - 如何提交一个带有“verified”标识的commit 前言(Why) 今天在Github上浏览某项目的commit记录的时候发现,有的commit记录带有verified绿色标识,有的带有橘色的Unverified标识,还有的什么都不显示。 既然我是根正苗红的作者(bushi)…...

idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
掌握 HTTP 请求:理解 cURL GET 语法
cURL 是一个强大的命令行工具,用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中,cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...

Android写一个捕获全局异常的工具类
项目开发和实际运行过程中难免会遇到异常发生,系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler,它是Thread的子类(就是package java.lang;里线程的Thread)。本文将利用它将设备信息、报错信息以及错误的发生时间都…...

AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)
Name:3ddown Serial:FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名:Axure 序列号:8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP...

华为云Flexus+DeepSeek征文 | 基于Dify构建具备联网搜索能力的知识库问答助手
华为云FlexusDeepSeek征文 | 基于Dify构建具备联网搜索能力的知识库问答助手 一、构建知识库问答助手引言二、构建知识库问答助手环境2.1 基于FlexusX实例的Dify平台2.2 基于MaaS的模型API商用服务 三、构建知识库问答助手实战3.1 配置Dify环境3.2 创建知识库问答助手3.3 使用知…...
python打卡第48天
知识点回顾: 随机张量的生成:torch.randn函数卷积和池化的计算公式(可以不掌握,会自动计算的)pytorch的广播机制:加法和乘法的广播机制 ps:numpy运算也有类似的广播机制,基本一致 **…...
Go 语言中switch case条件分支语句
1. 基本语法 package main import "fmt" func main() {var extname ".css"switch extname {case ".html":fmt.Println("text/html")case ".css":fmt.Println("text/css") // text/csscase ".js":fmt.…...