在这里插入图片描述

文章目录

  • 快速排序
    • 1.hoare版本
      • 算法优化
        • 三数取中法
        • 小区间优化
      • 完整代码如下
      • 算法分析
      • 时间复杂度
      • 空间复杂度
    • 2.前后指针法
      • 排序过程
    • 3.非递归(栈模拟)
      • 实现思路
  • 总结

快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右 子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

1.hoare版本

在这里插入图片描述

简单来说就是选某个元素为基准值(这里先默认第一个),然后把比基准值小的都放到基准值的左边,比基准值大的都放到基准值的右边

以下图为例

在这里插入图片描述

先以6为基准

然后左边找大,右边找小,之后互换

进行这么一趟后,6左边就都比它小,右边都比它大

然后以6为分界线,再分成两个区间,类似于二叉树

在这里插入图片描述

void QuickSort(int* a, int left, int right) {if (left >= right) return;//区间不存在时直接返回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[end]);// 递归排序子数组QuickSort(a, left, end - 1);QuickSort(a, end + 1, right);}
}

算法优化

虽然基本快速排序已经很快,但在某些情况下性能会下降,特别是当输入数组已经有序或接近有序时。以下是两种常见的优化策略:

三数取中法

当数组已经有序或接近有序时,选择第一个元素作为基准会导致分区极度不平衡,从而使算法退化为 O(n²) 的时间复杂度。三数取中法通过选择左端、中间和右端三个元素的中值作为基准,可以有效避免这种最坏情况。

三数取中代码如下

int GetMidi(int* a, int left, int right)//左边作key 右边先走
{int midi = (left + right) / 2;if (a[left] < a[midi]){if (a[midi] < a[right]){//说明midi是中间值return midi;}//走到这说明midi最大,所以剩下两个数中大的就是中间值else if(a[left]>a[right]){return left;}else//剩下最后一种情况 不必多说{return right;}}else//走到这说明 a[left]>a[midi]{if (a[midi] > a[right]){return midi;}//到这说明midi最小 所以找剩下两个小的else if (a[left] < a[right]){return left;}else{return right;}}
小区间优化

区间比较小时,递归代价比较大,所以要进行小区间优化

对于递归来说

最后一层递归次数占总体的50%,倒数第二层25% ,所以进行小区间优化可以减少递归次数

在这里插入图片描述

	//区间比较小时。进行小区间优化,不再递归分割排序。减少递归次数if ((right - left + 1) < 10){InsertSort(a + left, right - left + 1);}

完整代码如下

#include<stdio.h>void Swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}
void InsertSort(int* a, int n) 
{for (int i = 1; i < n; i++) {int key = a[i];int j = i - 1;while (j >= 0 && a[j] > key) {a[j + 1] = a[j];j--;}a[j + 1] = key;}
}
//面试手撕快排 不用写小区间优化和三数取中 后续讲一下思路即可 不然会觉得你写的慢
//避免有序情况下效率降低
//1.随机数
//2。三数取中
int GetMidi(int* a, int left, int right)//左边作key 右边先走
{int midi = (left + right) / 2;if (a[left] < a[midi]){if (a[midi] < a[right]){//说明midi是中间值return midi;}//走到这说明midi最大,所以剩下两个数中大的就是中间值else if(a[left]>a[right]){return left;}else//剩下最后一种情况 不必多说{return right;}}else//走到这说明 a[left]>a[midi]{if (a[midi] > a[right]){return midi;}//到这说明midi最小 所以找剩下两个小的else if (a[left] < a[right]){return left;}else{return right;}}
}
void QuickSort(int* a, int left, int right) {if (left >= right) return;//小区间优化,不再递归分割排序。减少递归次数if ((right - left + 1) < 10){InsertSort(a + left, right - left + 1);}else{//三数取中int midi = GetMidi(a, left, right);Swap(&a[left], &a[midi]);int keyi = left;int begin = left + 1;  // 从基准下一个开始int end = right;while (begin <= end) {// 从右向左找小于基准的值while (begin <= end && a[end] >= a[keyi])end--;// 从左向右找大于基准的值while (begin <= end && a[begin] <= a[keyi])begin++;// 交换找到的不符合元素if (begin < end)Swap(&a[begin], &a[end]);}// 将基准放到正确位置Swap(&a[keyi], &a[end]);// 递归排序子数组QuickSort(a, left, end - 1);QuickSort(a, end + 1, right);}
}int main() {int arr[3] = { 2, 1, 3 };QuickSort(arr, 0, 2);for (int i = 0; i < 3; i++) {printf("%d ", arr[i]);  // 输出:1 2 3}return 0;
}

为什么要右边先走呢

分析如下图

在这里插入图片描述

算法分析

时间复杂度

O(n log n):一共有logn层递归 每一层都是所有子数组加起来都是n

空间复杂度

  • 最佳情况:O(log n) - 递归调用的栈空间
  • 最坏情况:O(n) - 当分区极度不平衡时

2.前后指针法

前后指针法是快速排序的另一种常见实现方式,通过两个指针prev和cur来遍历数组,将小于基准值的元素移动到前面。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

排序过程

  • 定义两个指针prev和cur,prev指向left,cur指向prev+1。
  • cur指针向右移动,如果a[cur]小于基准值,则prev++并交换a[prev]和a[cur]。
  • 最后将基准值交换到prev位置。

排序一趟的代码如下

int prev = left;
int cur = prev+1;
while(cur<=right)
{if(a[cur]<a[keyi]&&++prev!=cur)//cur比基准小就交换Swap(&a[prev],&a[cur])//cur不管交换还是不交换,都要继续走cur++
}
Swap(&a[prev],&a[keyi]);
return prev;

完整代码

int PartSort2(int* a, int left, int right)
{// 三数取中int midi = GetMidi(a, left, right);Swap(&a[left], &a[midi]);int keyi = left;int prev = left;int cur = prev+1;while (cur <= right){if (a[cur] < a[keyi] && ++prev != cur)Swap(&a[prev], &a[cur]);cur++;}Swap(&a[prev], &a[keyi]);return prev;
}
void QuickSort(int* a, int left, int right)
{if (left >= right)return;int keyi = PartSort1(a, left, right);// [left, keyi-1] keyi [keyi+1, right]QuickSort(a, left, keyi - 1);QuickSort(a, keyi + 1, right);
}

3.非递归(栈模拟)

递归实现虽然简洁,但在处理大规模数据时可能导致栈溢出。非递归实现通过显式栈来模拟递归过程,避免递归带来的栈开销。

实现思路

  1. 初始化一个栈,将初始左右下标入栈(先右后左)。
  2. 循环取出栈顶的左右下标,进行分区操作。
  3. 将分区后的左右子数组下标入栈,继续处理直到栈为空。

在这里插入图片描述

void QuickSortNonR(int* a, int left, int right)
{ST st;STInit(&st);STPush(&st, right);//先入右 再入左STPush(&st, left);while (!STEmpty(&st)){int begin = STTop(&st);STPop(&st);int end = STTop(&st);STPop(&st);int keyi = PartSort2(a, begin, end);// [begin, keyi-1] keyi [keyi+1, end]if (keyi + 1 < end){STPush(&st, end);STPush(&st, keyi+1);}if (begin < keyi-1){STPush(&st, keyi-1);STPush(&st, begin);}}STDestroy(&st);
}

总结

快速排序是一种高效且应用广泛的排序算法,通过分治策略将问题分解为子问题处理。其性能取决于基准值的选择是否合理,因此在实际应用中常采用三数取中等优化策略来避免最坏情况的发生。

无论是递归还是非递归实现,快速排序都能在平均情况下达到O(n log n)的时间复杂度,使其成为处理大规模数据的理想选择之一

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/95153.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/95153.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/95153.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在ROS中获取并发布UBS式传感器的温湿度

哈喽大家好&#xff0c;我是钢板兽&#xff01; 今天更新一篇和ROS相关的文章&#xff0c;有个项目需求是在ROS中获取并发布UBS式传感器的温湿度&#xff0c;我使用的温湿度传感器简介如下&#xff1a;DL11- MC-S1 温湿度传感器通过USB 接口采用标准MODBUS RTU 协议通信&#x…

【图论】 Graph.jl 操作汇总

文章目录图论的集合类操作Base.getindexBase.intersectBase.joinBase.reverseBase.reverse!Base.sizeBase.sumBase.sumBase.union图生成与转换Graphs.cartesian_productGraphs.complementGraphs.compute_shiftsGraphs.crosspathGraphs.differenceGraphs.egonetGraphs.induced_s…

【链表 - LeetCode】146. LRU 缓存

146. LRU 缓存 题解&#xff1a; class LRUCache {list<pair<int,int>>v;unordered_map<int,list<pair<int,int>>::iterator>idx;int capacity; public:LRUCache(int capacity):capacity(capacity){}int get(int key) {if(idx.count(key) 0) …

Elasticsearch vs Solr vs OpenSearch:搜索引擎方案对比与索引设计最佳实践

Elasticsearch vs Solr vs OpenSearch&#xff1a;搜索引擎方案对比与索引设计最佳实践 随着大数据和实时分析需求的爆发&#xff0c;搜索引擎已成为许多业务系统中的核心组件。本篇文章将从“技术方案对比分析型”角度切入&#xff0c;重点比较三大主流搜索引擎&#xff1a;El…

光颉科技)Viking)的CS25FTFR009 1225 0.009R/9mR 3W电阻介绍-华年商城

“**华年商城”**小编为您介绍&#xff1a;光颉科技&#xff08;Viking&#xff09;的CS25FTFR009 1225 0.009R/9mR 3W电阻 光颉CS25FTFR009合金电阻&#xff1a;0.009Ω/9mΩ 3W 1%精密采样电阻 光颉科技&#xff08;Viking&#xff09;的CS25FTFR009是一款高性能的电流检测电…

港科大开放世界长时域具身导航!LOVON:足式机器人开放词汇目标导航

作者&#xff1a;Daojie Peng1^{1}1, Jiahang Cao1,2^{1,2}1,2, Qiang Zhang1,2^{1,2}1,2, Jun Ma1,3^{1,3}1,3单位&#xff1a;1^{1}1香港科技大学&#xff08;广州&#xff09;&#xff0c;2^{2}2北京人形机器人创新中心&#xff0c;3^{3}3香港科技大学论文标题&#xff1a;L…

【前端教程】JavaScript 数组对象遍历与数据展示实战

在前端开发中&#xff0c;处理数组和对象是日常工作的基础。无论是篇文章将通过一个具体案例&#xff0c;详细讲解如何使用JavaScript遍历包含对象的数组&#xff0c;并将数据以清晰的格式展示在页面上。我们会从基础语法开始&#xff0c;逐步优化代码&#xff0c;最终实现一个…

无重复字符的最长子串,leetCode热题100,C++实现

题目来源&#xff1a;leetCode 3. 无重复字符的最长子串 - 力扣&#xff08;LeetCode&#xff09; 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长 子串 的长度。 解法 class Solution { public:int lengthOfLongestSubstring(string s) {unordered_set<…

卷积神经网络中1×1卷积的作用

part I &#xff1a;来源part II &#xff1a;应用part III &#xff1a;作用&#xff08;降维、升维、跨通道交互、增加非线性&#xff09;part IV &#xff1a;从fully-connected layers的角度理解一、来源&#xff1a;[1312.4400] Network In Network &#xff08;如果11…

VMware设置Ubuntu虚拟机桥接模式完整教程

VMware 设置 Ubuntu 虚拟机桥接模式完整教程 下面是一个详细的、避免出错的 VMware Ubuntu 桥接模式设置教程&#xff0c;包含常见问题的解决方案。 准备工作 确保宿主机&#xff08;Windows 11&#xff09;已连接到网络&#xff08;有线或无线&#xff09;确认您有管理员权限关…

浅析NVMe协议:DIF

文章目录概述DIF数据格式盘片支持DIFFormatPILPIMSETLBAF协议命令DIF支持PRACTPRACT0PRACT1PRCHK相关参考概述 NVMe协议将DIF信息作为元数据的一部分进行携带。 DIF数据格式 DIF的PI由多个字段组成&#xff0c;包括&#xff1a; Guard字段&#xff1a;基于逻辑块数据计算的C…

【观成科技】蔓灵花User下载者加密通信分析

概述2025年5月7日&#xff0c;蔓灵花&#xff08;BITTER&#xff09;组织针对巴基斯坦电信公司工作人员发起钓鱼邮件攻击&#xff0c;投递伪装为安全简报的恶意邮件&#xff0c;附件为IQY类型的Web查询文件。该文件在用户执行后通过HTTP协议获取远程CMD指令并执行&#xff0c;进…

Redis 保证数据不丢失

Redis 保证数据不丢失&#xff08;或最大限度减少丢失&#xff09;的核心是通过 持久化机制 结合 合理的配置策略 实现的。具体方案如下&#xff1a;一、核心&#xff1a;开启 Redis 持久化&#xff08;防止进程崩溃丢失数据&#xff09;Redis 提供两种持久化方式&#xff0c;可…

NUMA/SNC 4种组合下Stream+MLC性能对决:双路服务器BIOS调优全攻略

关于调整 BIOS NUMA 与 SNC 选项的 Stream / MLC 性能测试总结一、测试背景与目的在现代多路 Intel Xeon 服务器上&#xff0c;NUMA&#xff08;Non-Uniform Memory Access&#xff09;与 SNC&#xff08;Sub-NUMA Clustering&#xff09;是两项决定内存访问延迟与带宽的关键 B…

Java-113 深入浅出 MySQL 扩容全攻略:触发条件、迁移方案与性能优化

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; AI炼丹日志-31- 千呼万唤始出来 GPT-5 发布&#xff01;“快的…

Kafka Connect + Streams 用到极致从 CDC 到流处理的一套落地方案

关键目标&#xff1a; 零丢失&#xff1a;端到端 Exactly Once&#xff08;Source 端事务 Streams exactly_once_v2 Sink DLQ&#xff09;。低延迟&#xff1a;Producer 端批量压缩 Streams 缓存 合理 poll/commit 间隔。可恢复&#xff1a;Connect/Streams 的 rebootstrap…

# `std::basic_istream`总结

std::basic_istream总结 文章目录std::basic_istream总结概述常用类型定义全局对象核心成员函数1. 格式化输入2. 非格式化输入3. 流定位4. 其他功能继承的功能来自 std::basic_ios状态检查状态管理来自 std::ios_base格式化标志流打开模式特点说明例子std::basic_istream全面用…

人工智能——课程考核

课程考核包括平时测验&#xff08;75%&#xff09;和讨论&#xff08;25%&#xff09;两个环节&#xff0c;测验采用线上随堂考试&#xff08;2-3次&#xff0c;具体会在本课堂发布&#xff09;重点考核&#xff1a;A*算法、极大极小过程&#xff08;α-β剪枝&#xff09;、不…

机器学习-时序预测1

最近面试过程中&#xff0c;Predict-then-Optimize是运筹优化算法工程师未来的发展方向。就像我之前写过的运筹优化&#xff08;OR&#xff09;-在机器学习&#xff08;ML&#xff09;浪潮中何去何从&#xff1f;-CSDN博客&#xff0c;机器学习适合预测、运筹优化适合决策。我研…

vim-plugin AI插件

文章目录一、vim 插件管理vim-plug二、如何使用和配置 vim-plug第 1 步&#xff1a;安装 vim-plug第 2 步&#xff1a;配置你的 .vimrc / init.vim第 3 步&#xff1a;安装插件常用 vim-plug 命令三、配置vim-aivim-aivim-deepseekvim升级四、配置 AI 插件GitHub Copilot第 1 步…