【八大排序(六)】快排终极篇-快速排序非递归版

简介: 【八大排序(六)】快排终极篇-快速排序非递归版

💓博主CSDN主页:杭电码农-NEO💓


⏩专栏分类:八大排序专栏


🚚代码仓库:NEO的学习日记🚚


🌹关注我🫵带你学习排序知识

  🔝🔝


1. 前情回顾

在学习快排非递归版前
我们要先了解快排的思想和栈的结构
可以跳转快排初阶栈详解复习一下

非递归版的快排只是优化了递归函数
然而单趟快排所用的思想还是不变的
只不过将递归过程转化为了循环

注:本章单趟快排使用最左边做为基准值


2. 快排非递归基本思路

我们先定义一个无序数组:

int a[]={6,1,2,7,9,3,4,5,10,8};

一共十个元素,下标范围:0 ~ 9

基本思路:

  • 第一次快排要遍历下标0~9的元素
  • 将 0 和 9入栈后使用完再出栈
  • 假设第一趟快排分了两个子区间为
  • 0~ 4和6~ 9.将6,9入栈后再将0,4入栈
  • 栈顶为0,4,单趟快排就遍历下标为0~4
  • 使用完后0,4出栈,并且将子区间入栈
  • 以此类推直到栈中没有元素

画图理解:


3. 对非递归思路的解释

思考:

  1. 非递归的优势是什么?
  2. 为什么要用栈结构?
  3. 为什么右子区间要先入栈?
  4. 怎么判断左右子区间的小标范围?

解释:


非递归的优势是无论预排序数组多长
都不会产生栈溢出问题.
而递归层数太深,系统栈帧空间不足
就会发生栈溢出的错误


使用栈结构是由于栈的特殊性质:
栈后进先出的特性可以使数组:
左子区间全部排好序再排右子区间.
这和递归的过程保持一致.


如果入栈顺序乱来,也可以排好序
只不过代码和思想不容易被理解


我们设计每一次单趟排序返回
基准值所在位置对应的下标
而下标加一为右子区间的开始
下标减一为左子区间的截至

(注:单趟快排可以使用任意方法)

方法详解:

hoare办法

挖坑法,指针法


4. 单趟快排代码实现

我们假设这里使用指针法做单趟快排

前后指针法:

//单趟快排(前后指针版本)
int Partion3(int* a, int left, int right)
{
  //三数取中--面对有序的情况不会栈溢出(key不会选到最大或者最小的数)
  int mini = GetMidIndex(a, left, right);
  swap(&a[left], &a[mini]);
  int key = left;
  int prev = left;
  int cur = left + 1;
  while (cur <= right)
  {
    while (a[cur] >= a[key] && cur <= right)//cur指针找小于key的
    {
      ++cur;
    }
    if (cur <= right)
    {
      swap(&a[++prev], &a[cur]);
      cur++;//交换完后,cur要再往后走一步
    }
  }
  swap(&a[key], &a[prev]);// 最后交换prev和key的值
  return prev;//返回基准值的位置,方便下次递归
}

5. 快排非递归代码实现

非递归代码:

//快速排序(非递归)
void QuickSortNonR(int* a, int left, int right)
{
  ST st;
  StackInit(&st);
  StackPush(&st, left);
  StackPush(&st, right);
  while (!StackEmpty(&st))//当栈不为空时就继续
  {
    int end = StackTop(&st);//将栈顶元素赋值给下标遍历的尾
    StackPop(&st);//删除栈顶元素
    int begin = StackTop(&st);//再将新的栈顶元素给下标遍历的头
    StackPop(&st);//再把这个值删除
    int key = Partion3(a, begin, end);//单趟快排返回基准值对应位置下标
    if (key < end)
    {
      StackPush(&st, key + 1);
      StackPush(&st, end);
    }
    if (begin < key - 1)
    {
      StackPush(&st, begin);
      StackPush(&st, key);
    }
  }
  StackDestroy(&st);
}

6. 总结思考

总的来说,学计算机专业的同学
掌握快速排序是必须的.
但是大部分人都只会递归版本的快排

如果在面试的时候你现场给面试官

手撕一个非递归的快速排序

那么你一定会在人群中脱颖而出!


🔎 下期预告:归并排序初级篇 🔍


相关文章
|
设计模式 监控 安全
JUC第一讲:Java并发知识体系详解 + 面试题汇总(P6熟练 P7精通)
JUC第一讲:Java并发知识体系详解 + 面试题汇总(P6熟练 P7精通)
3612 0
|
SQL 监控 关系型数据库
【MYSQL高级】Mysql找出执行慢的SQL【慢查询日志使用与分析】
【MYSQL高级】Mysql找出执行慢的SQL【慢查询日志使用与分析】
4961 0
|
11月前
|
SQL 关系型数据库 MySQL
大厂面试官:聊下 MySQL 慢查询优化、索引优化?
MySQL慢查询优化、索引优化,是必知必备,大厂面试高频,本文深入详解,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验分享。
大厂面试官:聊下 MySQL 慢查询优化、索引优化?
|
11月前
|
存储 分布式计算 流计算
实时计算 Flash – 兼容 Flink 的新一代向量化流计算引擎
本文介绍了阿里云开源大数据团队在实时计算领域的最新成果——向量化流计算引擎Flash。文章主要内容包括:Apache Flink 成为业界流计算标准、Flash 核心技术解读、性能测试数据以及在阿里巴巴集团的落地效果。Flash 是一款完全兼容 Apache Flink 的新一代流计算引擎,通过向量化技术和 C++ 实现,大幅提升了性能和成本效益。
3394 73
实时计算 Flash – 兼容 Flink 的新一代向量化流计算引擎
|
9月前
|
Prometheus 监控 Cloud Native
高频面题: 你们线上 QPS 多少?你 怎么知道的?
本文由45岁资深架构师尼恩撰写,针对高级开发和架构师面试中的高频问题提供详细解答。文章涵盖了QPS、TPS、RT等性能指标的定义及计算方法,详解了如何配置Prometheus与Grafana监控系统QPS,并提供了应对高并发场景(如双十一抢购)的系统部署策略。此外,还分享了多个大厂面试真题及解决方案,帮助读者在面试中充分展示技术实力,提升求职竞争力。建议收藏并深入学习,为面试做好充分准备。更多内容可参考《尼恩Java面试宝典》及相关技术圣经系列PDF。
|
10月前
|
数据采集 前端开发 物联网
【项目实战】通过LLaMaFactory+Qwen2-VL-2B微调一个多模态医疗大模型
本文介绍了一个基于多模态大模型的医疗图像诊断项目。项目旨在通过训练一个医疗领域的多模态大模型,提高医生处理医学图像的效率,辅助诊断和治疗。作者以家中老人的脑部CT为例,展示了如何利用MedTrinity-25M数据集训练模型,经过数据准备、环境搭建、模型训练及微调、最终验证等步骤,成功使模型能够识别CT图像并给出具体的诊断意见,与专业医生的诊断结果高度吻合。
19127 7
【项目实战】通过LLaMaFactory+Qwen2-VL-2B微调一个多模态医疗大模型
|
消息中间件 NoSQL Kafka
订单超时取消的11种方式(非常详细清楚)
订单超时取消的11种方式(非常详细清楚)
7638 4
订单超时取消的11种方式(非常详细清楚)
|
消息中间件 存储 大数据
聊一聊几款主流消息队列之间的差异,我们应该如何选择
聊一聊几款主流消息队列之间的差异,我们应该如何选择
782 2
|
Ubuntu NoSQL Linux
一文讲明Docker的基本使用,常见Docker命令使用 、Docker的安装使用等【详细说明+图解+概念+实践】
这篇文章详细介绍了Docker的基本使用,包括Docker的安装、常用命令、架构概念等,并通过图解和实践帮助读者快速掌握Docker的使用方法。
一文讲明Docker的基本使用,常见Docker命令使用 、Docker的安装使用等【详细说明+图解+概念+实践】