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

快速排序的递归与非递归实现

快排的基本概念

快速排序就是找一个数为基准值,遍历数组,小于基准值的在基准值左边,

大于基准值的在基准值右边,等于基准值的可以在左边或右边,

之后把数组从基准值的位置分开,一左一右,等于基准值的不用在排了,只需排小于的左边,

和大于的右边,也可以随便放一边就行,只要不跨到另一边,逻辑就没问题。

缺点:重复数多的时候,效率会变差。

快速排序的两个效率优化

一、三数取中

基准值的如果直接选最左边的值,

如果最左边的值就是数组的最小值,那么遍历一遍后根本没分出左右,左边啥也没有,

就成了去掉一个基准值再排一遍,

如果等待排序的是一个有序的数组或者接近有序的数组

快速排序的速度就跟冒泡一样慢了,

所以为了处理接近有序的数组,基准值选取三个数中不大不小的数

三个数分别是最左边的,最右边的和数组中间的

三个数中选一下,时间很快,但就可以避免遇到有序数组和接近有序的数组

如果数组是有无序的,那么三数取中和随便选一个没区别

但如果接近有序了,那么三数取中得到的基准值大概就是中间值,可以把数组分为大小相似的左右区间

代码实现 返回中间大小的数的地址

//三个数比大小选中间的 int* san_zhong(int* a, int* b, int* c) { if (*a > *b) { if (*c > *a) { return a; } else if(*b>*c) { return b; } else { return c; } } else { if (*c > *b) { return b; } else if (*c > *a) { return c; } else { return a; } } }

二、小区间优化

字面意思,如果数组很小的话就不用快速排序了,直接用插入排序

很小是多小?小到使用插入排序比快速排序更快

如果是递归的快速排序,长度10的数组要递归好几次,

如果是非递归的快速排序也是用栈模拟递归

但如果用插入排序就不需要递归了

空间和时间上都要更快

一个10万的数组要递归大概14万次。

加上小区间优化递归后就只需要递归大约3万次,省很多事。

快速排序的递归实现

快速排序有

左右指针法,这是最先研究出来的,不好理解

挖坑法,比左右指针法好理解

前后指针法,俩指针一前一后往后走

三个目的都一样让小于基准值的在前面,大于基准值的在右面

只是方法不一样

左右指针法

//快速排序递归实现+三选一优化+小区间插入排序优化 void kuai_pai_1(int* a, int left, int right) { if (left >= right) return; if (right - left + 1 < 11) { cha_ru(a + left, right - left + 1); return; } int zhong = left; int leftt = left; int rightt = right; int * q = san_zhong(&a[(left+right)/2],&a[left],&a[right]); jiao_huan(&a[zhong], q); while (left != right) { while (left != right && a[right] > a[zhong]) { right--; } while (left != right && a[left] <= a[zhong]) { left++; } jiao_huan(&a[left], &a[right]); } jiao_huan(&a[left], &a[zhong]); kuai_pai_1(a, leftt, left - 1); kuai_pai_1(a, right + 1, rightt); return; }

右指针往左走,遇到不大于基准值的数停下

左指针往右走,遇到比基准值大的数停下

之后交换俩数,没相遇就重复

最后俩指针相遇时停下的位置

停下的位置一定小于等于基准值

因为如果是右指针停下,左指针走的时候遇到右指针停下,右指针一定是在小于等于基准值的数上

左子针停下,是刚交换完右指针才走,左子针也停在小于等于基准值的数上,所以

停下的位置一定小于等于基准值

挖坑法

// 快速排序挖坑法 void kuai_pai_2(int* a, int left, int right) { if (left >= right) return; if (right - left + 1 < 11) { cha_ru(a + left, right - left + 1); return; } int leftt = left; int rightt = right; int* q = san_zhong(&a[(left + right) / 2], &a[left], &a[right]); jiao_huan(&a[left], q); int zhong = a[left]; while (left < right) { while (left < right && a[right] > zhong) { right--; } a[left] = a[right]; while (left < right && a[left] <= zhong) { left++; } a[right] = a[left]; } a[left] = zhong; kuai_pai_2(a, leftt, left - 1); kuai_pai_2(a, right + 1, rightt); return; }

开始存一下基准值,这样左边就算有一个坑

右指针左移动找小的,找到了把这个值放到左边挖好的坑里,这里就算新的坑,

左指针右移找大的,找到了把这个值给右指针的坑

最后相遇在一个坑里

把基准值的值放入坑中

跟左右指针法差不多,更好理解

前后指针法

// 快速排序前后指针法 void kuai_pai_3(int* a, int left, int right) { if (left >= right) return; if (right - left + 1 < 11) { cha_ru(a + left, right - left + 1); return; } int* q = san_zhong(&a[(left + right) / 2], &a[left], &a[right]); jiao_huan(&a[left], q); int qian = left; int hou = left+1; int zhong = a[left]; while (hou <= right) { if (a[hou] <= zhong&&++qian<hou) { jiao_huan(&a[qian], &a[hou]); } hou++; } jiao_huan(&a[qian], &a[left]); kuai_pai_3(a, left, qian-1); kuai_pai_3(a, qian + 1, right); return; }

前指针指向最左边,后指针指向最左边后一位

如果后指针指向了比基准值小的或一样的数,就前后指针一起往后移动一格,前指针先移动,

如果跟后指针位置不同就交换指向的数,

后指针若指向的大的数,前指针就不动了,

前指针移动的步数就是小于等于基准值的数的数量,第一个数是基准值,

后指针找到大的值,就会让前指针停下,等到后指针找到小的值,才会让前指针移动一步

交换后就把大的数移后面去了,

后指针走出数组,前指针的位置放入基准值,开头的位置放前指针指向的位置的值。

快速排序非递归法

// 快速排序 非递归实现 void kuai_pai_4(int* a, int lefttt, int righttt) { Stack arr; StackInit(&arr); StackPush(&arr, righttt); StackPush(&arr, lefttt); while (!StackEmpty(&arr)) { // 获取栈顶元素 int left = StackTop(&arr); // 出栈 StackPop(&arr); // 获取栈顶元素 int right = StackTop(&arr); // 出栈 StackPop(&arr); if (left >= right) { continue; } if (right - left + 1 < 11) { cha_ru(a + left, right - left + 1); continue; } int zhong = left; int leftt = left; int rightt = right; int* q = san_zhong(&a[(left + right) / 2], &a[left], &a[right]); jiao_huan(&a[zhong], q); while (left != right) { while (left != right && a[right] > a[zhong]) { right--; } while (left != right && a[left] <= a[zhong]) { left++; } jiao_huan(&a[left], &a[right]); } jiao_huan(&a[left], &a[zhong]); StackPush(&arr, left-1); StackPush(&arr, leftt); StackPush(&arr, rightt); StackPush(&arr, right+1); } StackDestroy(&arr); }

就是建造个栈,把需要递归的区间放入栈中,模拟递归,取一个区间,排完之后基准值的左区间和右区间放入栈,一直循环直到栈空了。把递归改为出栈入栈。

http://www.jsqmd.com/news/805714/

相关文章:

  • 开发者必备:命令行优先的备忘录与代码片段管理工具Mnemon
  • 2026年高强级反光膜全攻略:三类反光膜、二类反光膜、五类反光膜、交通标志杆件、人防标牌、反光交通标牌、反光膜加工选择指南 - 优质品牌商家
  • 手把手带你拿下ElevenLabs Creator认证:从环境配置、语音样本提交到模型定制部署的完整流水线(含GitHub可运行脚本)
  • 2026年5月自贡建筑装饰选材指南:为何任鸟飞成为发泡陶瓷雕花口碑之选? - 2026年企业推荐榜
  • ARM MPMC静态内存控制器架构与寄存器配置详解
  • 2026Q2线上百货加盟权威选择:前置仓加盟/投资即使零售平台/投资线上百货超市/投资网上超市/投资网络超市/本低仓加盟/选择指南 - 优质品牌商家
  • 未来已来:AI驱动的数据湖仓
  • 基于OpenTelemetry的Gemini CLI本地数据驾驶舱部署与实战指南
  • 2026现阶段西安防水堵漏公司深度**:远大加固为何成为行业优选? - 2026年企业推荐榜
  • 基于MCP协议的AssistAI:深度集成Eclipse的AI编程助手实战指南
  • 长沙定制开发本地生活APP打造城市便民消费场景
  • 2篇3章3节:Trae 的高效小说创作与文件管理实操
  • “找档难、找档慢”困扰工作?档案宝智能检索功能,让档案查询秒响应
  • DeepSeek总结的pg_clickhouse v0.3.0的新特性
  • 基于 ESP32-S3 的四博 AI 墨水屏智能音箱方案:CozyLife、Find My、Google 防丢与 MCP 工具控制
  • AMD Ryzen调试神器:SMU Debug Tool终极指南,轻松掌控CPU性能
  • 2026年长沙名表珠宝抵押机构TOP推荐:长沙高档礼品回收、长沙K金回收、长沙包包鉴定、长沙名包回收、长沙名包抵押选择指南 - 优质品牌商家
  • 2026年苏州兼职会计代账选型:苏州兼职会计代账、苏州外贸公司代理记账、苏州注册公司地址挂靠、苏州注册园区地址挂靠选择指南 - 优质品牌商家
  • 黎阳之光:视频孪生硬核赋能,共启数字孪生水利监测新征程
  • ETS2LA:为《欧洲卡车模拟2》带来终极智能驾驶体验的5大核心功能
  • 终极指南:如何为Photoshop安装AVIF插件实现高效图像处理
  • Godot开发者必备:awesome-godot资源库高效使用指南
  • 开源项目可持续融资:Polar自托管部署与GitHub集成实战
  • 基于RAG与LLM构建多文档智能问答系统:从原理到实践
  • 白嫖新网免费云主机,挂QQ机器人亲测可用
  • 2026道岔权威厂家推荐:轨道道岔、道岔尖轨、重轨道岔、钢轨道岔、铁路道岔、9号道岔、cz2209道岔、交叉渡线道岔选择指南 - 优质品牌商家
  • C语言指针:从零掌握指针(5) 完结篇
  • 2026年当下,成都路虎专业保养如何选?深度解析“007至臻汽车”服务价值 - 2026年企业推荐榜
  • OpenClaw狂欢暗藏安全隐患,深圳机密计算科技端云一体方案筑牢AI Agent安全基座
  • 从零开始通过taotoken平台文档快速完成首个ai对话应用的原型开发