快速排序【hoare版本】【挖坑法】【双指针法】(数据结构)
快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
一、hoare版本
该算法的大体框架为:假设取数组的头为key同时保存索引变量begin的值在此处,取key的另一端为索引变量end,end不断向左移动,直至处于一个小于key的值的位置,此时再让索引变量begin不断向右移动,直至处于一个大于key的值的位置,交换两个值。由此不断循环往复,最终肯定会在一个大于key值的位置处end和begin相遇,最终交换该值与key所在的位置,即可得到一个左边比key小,右边比key大的一个数组。
由于数组是以key为基准,左右两边分别小于和大于key,所以该数组又可分为[left,key-1] , [key+1,right],然后递归调用上面的算法,左右子序列重复该过程,直至所有元素都排列在相应位置上即可。
下图为单趟快速排序的过程

程序源代码
//单趟排序
int PartSort1(int* a, int begin, int end)
{int key = a[begin];//选取左边做keyint keyindex = begin;while (begin < end){while (begin < end && a[end] >= key)//此处必须要在此判断begin<end,防止end越界{end--;}while (begin < end && a[begin] <= key){begin++;}Swap(&a[end], &a[begin]);}//此时begin == endSwap(&a[end], &a[keyindex]);//此处不能跟key交换,因为key是局部变量return begin;
}
void QuickSort(int* a, int left , int right)
{assert(a);if (left >= right){return;}int div = PartSort1(a, left, right);//[left,div - 1] div [div + 1 , right]QuickSort(a, left, div - 1);QuickSort(a, div + 1, right);
}
注意事项
1.若选取右边的值做key,那么一定要让左边的begin先走,这样能保证他们性欲的位置一定是一个比key大的位置...(选取左边值做key同理)
2.注意在移动begin和end的时候每次都需要判断begin<end,防止begin和end相遇之后错过,无法进行正确的排序
优化
经过分析我们发现:快速排序的最坏情况是每次选择的基准元素都是当前子数组的最大或最小值,导致每次划分只能减少一个元素的规模。在这种情况下,算法的时间复杂度接近于O(N*N),快速排序也就没有什么优势了,因此我们要做出优化。
为避免选取到最大值或者最小值,出现了三数取中法。即选取三个数的中间值作为基准,就可以很好地避免这种情况。改进后的代码变为:
//三数取中法
int GetMidIndex(int* a, int left, int right)
{int mid = (left + right) / 2;if (a[left] < a[mid]){if (a[right] < a[left]){return left;}else if (a[right] > a[mid]){return mid;}else{return right;}}else//a[left] > a[mid]{if (a[left] < a[right]){return left;}else if (a[mid] > a[right]){return mid;}else{return right;}}}
//单趟排序
int PartSort1(int* a, int begin, int end)
{int midindex = GetMidIndex(a, begin, end);Swap(&a[midindex], &a[begin]);int key = a[begin];//选取左边做keyint keyindex = begin;while (begin < end){while (begin < end && a[end] >= key){end--;}while (begin < end && a[begin] <= key){begin++;}Swap(&a[end], &a[begin]);}//此时begin == endSwap(&a[end], &a[keyindex]);//此处不能跟key交换,因为key是局部变量return begin;
}
void QuickSort(int* a, int left , int right)
{assert(a);if (left >= right){return;}int div = PartSort1(a, left, right);//[left,div - 1] div [div + 1 , right]QuickSort(a, left, div - 1);QuickSort(a, div + 1, right);
}
二、挖坑法
挖坑法是最主流的快速排序的方法,因为其易于理解。挖坑法,顾名思义,就是要把数据挖出来,假设以最左端位置为坑,把它的值保存在一个变量key内,定义索引变量begin和end,使end不断向左边移动,直至a[end]的值小于key,把它放在坑内,然后begin再向右移动,直至a[begin]的值大于key,把它放在a[end]的坑内。由此循环往复,我们可以得到一个与上面排序相同的结果。
程序源代码
/挖坑法
int PartSort2(int* a, int begin, int end)
{int midindex = GetMidIndex(a, begin, end);Swap(&a[midindex], &a[begin]);int key = a[begin];//坑while (begin < end){while (begin < end && a[end] >= key){end--;}a[begin] = a[end];while (begin < end && a[begin] <= key){begin++;}a[end] = a[begin];}//begin == enda[begin] = key;return begin;
}
void QuickSort(int* a, int left , int right)
{assert(a);if (left >= right){return;}int div = PartSort3(a, left, right);//[left,div - 1] div [div + 1 , right]QuickSort(a, left, div - 1);QuickSort(a, div + 1, right);
}
注意事项
在移动begin和end的时候,同样要每次判断begin是否小于end,防止begin和end错过
三、双指针法
双指针法相对来说较难理解一点。我们要取最后一个元素为key,定义两个变量cur,prev,其中cur是数组的首元素的索引(begin),prev位于数组首元素的前一个位置,即begin - 1。算法的思想是:cur向右不断移动,直至找到a[cur] < key,++prev,然后让prev所在位置与cur所在位置的值进行交换。依次重复这个过程,我们也可以得到跟上面两种方法相同的结果。
程序源代码
//双指针法
int PartSort3(int* a, int begin, int end)
{int midindex = GetMidIndex(a, begin, end);Swap(&a[midindex], &a[begin]);int key = a[end];int cur = begin;int prev = begin - 1;while (cur <= end){while (cur<= end && a[cur] >= key)//注意!!!{cur++;}if (cur <= end){prev++;Swap(&a[cur], &a[prev]);cur++;}}Swap(&a[++prev], &a[end]);return prev;
}
void QuickSort(int* a, int left , int right)
{assert(a);if (left >= right){return;}int div = PartSort3(a, left, right);//[left,div - 1] div [div + 1 , right]QuickSort(a, left, div - 1);QuickSort(a, div + 1, right);
}
代码也可以换一种形式来呈现:
//双指针法
int PartSort3(int* a, int begin, int end)
{int prev = begin - 1;int cur = begin;int keyindex = end;while (cur <= end){if (a[cur] < a[keyindex] && a[cur] != a[keyindex]){Swap(&a[cur], &a[++prev]);}cur++;}Swap(&a[keyindex], &a[++prev]);return prev;
}
void QuickSort(int* a, int left , int right)
{assert(a);if (left >= right){return;}int div = PartSort3(a, left, right);//[left,div - 1] div [div + 1 , right]QuickSort(a, left, div - 1);QuickSort(a, div + 1, right);
}
注意事项
在初始化prev的值时,不能将其初始化为-1,要初始化为begin-1。初始化为-1,会导致子数列在进行递归时出现问题。
今天的分享到这就结束了,欢迎持续关注,有问题可以私信交流~
相关文章:
快速排序【hoare版本】【挖坑法】【双指针法】(数据结构)
快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均…...
class_5:在c++中一个类包含另一个类的对象叫做组合
#include <iostream> using namespace std;class Wheel{ public://成员数据string brand; //品牌int year; //年限//真正的成员函数void printWheelInfo(); //声明成员函数 };void Wheel::printWheelInfo() {cout<<"我的轮胎品牌是:"<…...
Linux - No space left on device
问题描述 No space left on device 原因分析 说明在服务器设备上的存储空间已经满了,不能再上传或者新建文件夹或者文件等。 解决方案 确认查看服务器系统的磁盘使用情况是否是真的已经没有剩余空间,复制下面命令在服务器上运行,然后发现如果…...
STC8H8K蓝牙智能巡线小车——2. 点亮左右转弯灯与危险报警灯
任务调用示例 RTX 51 TNY 可做多任务调度,API较为简单。 /* 接口API */// 创建任务 extern unsigned char os_create_task (unsigned char task_id); // 结束任务 extern unsigned char os_delete_task (unsigned char task_id);// 等待 extern unsig…...
【微信小程序独立开发 3】个人资料页面编写
这一节完成用户个人信息昵称的填写和获取 上节编写完成后的页面如下所示: 首先进行用户昵称编辑功能的编写,铲屎官昵称采用了navigator标签,当点击昵称时会自动跳转到昵称编辑页面。 首先输入昵称编辑界面的导航栏名称 {"usingCompone…...
Linux笔记:Linux中的文件系统权限
在Red Hat Enterprise Linux 或其他类似的Linux发行版中,全局umask设置通常在几个不同的系统级配置文件中定义。以下是一些可能设置umask的地方: (1)/etc/profile: 这是为系统上的所有用户设置全局环境变量和启动程序的地方。通…...
Android基于Matrix绘制PaintDrawable设置BitmapShader,以手指触点为中心显示原图的圆切图,Kotlin(4)
Android基于Matrix绘制PaintDrawable设置BitmapShader,以手指触点为中心显示原图的圆切图,Kotlin(4) 这篇 Android基于Matrix绘制PaintDrawable设置BitmapShader,以手指触点为中心显示原图像圆图,Kotlin&am…...
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
问题背景 当我们尝试通过SSH(Secure Shell)连接到远程服务器时,有时会遇到一个警告信息:“WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!”。这个消息表明SSH客户端检测到远程主机的身份(即其SSH密钥)…...
深入理解JVM虚拟机第三十九篇:JVM中新生代和老年代相关参数设置
😉😉 欢迎加入我们的学习交流群呀! ✅✅1:这是孙哥suns给大家的福利! ✨✨2:我们免费分享Netty、Dubbo、k8s、Mybatis、Spring、Security、Docker、Grpc、消息中间件、Rpc、SpringCloud等等很多应用和源码级别高质量视频和笔记资料,你想学的我们这里都有! 🥭🥭3:…...
打造创新的金融数据平台,加速数字化和智能化转型丨PingCAP 官网金融行业专区上线
自诞生以来,TiDB 的原生分布式架构在强一致性、高可用性和可扩展性等方面与金融级业务需求高度契合,早期版本即为包括北京银行在内的金融用户提供服务。 TiDB 的核心能力始终源自与中国金融用户的共同创造。作为金融级分布式数据库,TiDB 在国…...
记ubuntu2004通过NetworkManager修改网络的优先级
这里写自定义目录标题 前言步骤 前言 起因在于万恶的校园网,突然台式有线死活没法认证(感觉是IP冲突了?另外一台电脑同样的系统就没有问题,连路由器WIFI也是可以的,路由器设置的是桥接模式,有没有大佬提供…...
Android-常用数据结构和控件
HashMap 的原理 HashMap 的内部可以看做数组链表的复合结构。数组被分为一个个的桶(bucket)。哈希值决定了键值对在数组中的寻址。具有相同哈希值的键值对会组成链表。需要注意的是当链表长度超过阈值(默认是8)的时候会触发树化,链表会变成树形结构。 把握HashMap的…...
react使用recoil进行全局状态管理 + axios进行网络请求
我们尝试使用recoil进行全局状态管理以及axios进行网络请求。 recoil recoil是facebook官方推出的新的react状态管理方案,采用分散管理原子状态的设计模式,同时也强调immuteable(mobx则是mutable),这与react强调immu…...
基于Springboot的善筹网(众筹网-有报告)。Javaee项目,springboot项目。
演示视频: 基于Springboot的善筹网(众筹网-有报告)。Javaee项目,springboot项目。 项目介绍: 采用M(model)V(view)C(controller)三层体系结构,通过Spring S…...
【Python学习】Python学习14-函数
目录 【Python学习】Python学习14-函数 前言自定义函数创建语法自定义函数与调用参数传递参考 文章所属专区 Python学习 前言 本章节主要说明Python的函数。函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。 函数能提高应…...
C语言中对关键字和标识符的理解
1.关键字(keyword) 定义:被C语言赋予了特殊含义,用做专门用途的字符串(或单词)。 特点:全部关键字都是小写字母。 举例: int、return等已经被C语言定义好了。 传统的C语言(ANSI C࿰…...
基于Jackson封装的JSON、Properties、XML、YAML 相互转换的通用方法
文章目录 一、概述二、思路三、实现四、测试 一、概述 我们在 yaml转换成JSON、MAP、Properties 通过引入 实现了JSON、Properties、XML、YAML文件的相互转换,具体的类、方法如下: 上面的实现,定义了多个类、多个方法,使用太不…...
windows的换行符与linux风格的换行符不同的问题
问题展示: 说明: 出现这个错误的原因是脚本文件包含了windows风格换行符(‘\r\n’),而在linux环境下,通常使用unix风格的换行符(‘\n’).这个问题通常在windows环境下编辑脚本文件然…...
RK3568笔记九: DRM显示摄像头
若该文为原创文章,转载请注明原文出处。 一、介绍 学习DRM的目的是想做类似NVR显示多路实时流,通过勇哥(Marc)的指导,大概流程是通过Zlmedia拉流,RK3568的MPP解码,DRM显示,可以使用HDMI或DIS屏幕…...
简单明了,汽车级LM317系列LM317D2TR4G线性电压稳压器电源设计-参数应用方案分享
低压差线性稳压器(LDO),是指一种具有恒定电流输出电压的装置,主要由输入变压器、整流器、输出变压器三部分构成,工业原理为将输入的交流电压经过整流、滤波后得到直流输出电压,再经过控制元件和开关器件将稳…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
