数据结构——排序第三幕(深究快排(非递归实现)、快排的优化、内省排序,排序总结)超详细!!!!

在这里插入图片描述

文章目录

  • 前言
  • 一、非递归实现快排
  • 二、快排的优化版本
  • 三、内省排序
  • 四、排序算法复杂度以及稳定性的分析
  • 总结

前言

继上一篇博客基于递归的方式学习了快速排序和归并排序
今天我们来深究快速排序,使用栈的数据结构非递归实现快排优化快排(三路划分)
干货满满,上车

一、非递归实现快排

上篇博客基于递归实现了三个版本的快排,hoare版本,挖坑法,前后指针法
其实就是围绕基准值进行操作,不管哪一种版本,都离不开找基准值,递归得到子区间
快排的非递归版本也离不开找基准值,但是对区间进行了处理,使用到栈的数据结构

把一个大区间分成几个小区间
在这里插入图片描述
给定初始数据样例,我们正常使用前后指针的方法进行快排,找基准值
在这里插入图片描述
基准值,以及区间的下标
在这里插入图片描述

我们把0-2的区间左右下标入栈,4-5的区间下标入栈,相当于递归到子区间的操作
栈是遵循先进后出的规则,刚好和递归的区间的遍历顺序一样
每次前后指针找完基准值,就把分出来的左右区间下标入栈
但还是要注意越界的情况,比如基准值的节点在最左边或者最右边

假设基准值的下标为keyi,那么右区间就是[keyi+1,end],左区间就是[begin,keyi-1]
在这里插入图片描述
上图的有些区间就是不符合条件的

基本思路都叙述的差不多了,上代码

void QuickSortNonR(int* a, int left, int right)
{stack<int> st;   //  定义一个栈st.push(right);   //  这里先让右端下标入栈  因为栈是先进后出的st.push(left);		//    再让左端下标入栈  while (!st.empty())   {int begin = st.top();   //  取当前栈顶元素,也就是区间的左端 st.pop();int end = st.top();   //  取右端元素  st.pop();int prev = begin, cur = prev + 1;  // 然后就是前后指针找基准值 int keyi = begin;while (cur <= end){if (a[cur] < a[keyi] && ++prev != cur){swap(a[prev], a[cur]);}++cur;}swap(a[keyi], a[prev]);keyi = prev;         //  这里找到了基准值  if (keyi + 1 < end)  //  再根据基准值,分出左区间和右区间进行入栈 {st.push(end);st.push(keyi + 1);   //  右区间 }if (keyi - 1 > begin){st.push(keyi - 1);st.push(begin);      //  左区间   }}
}

非递归版本的快速排序就完成啦


二、快排的优化版本

快排的缺陷在上篇博客和大家讲过,如果数据有序或者数据全部相同的情况,快速排序的时间复杂度可能会到O(N^2)
这里对初始基准值的确定进行优化,如果数据有序,不从第一个数据取基准值
以及在前后指针的方法上引入三路划分,对相同的数据进行处理
其次三路划分针对有大量重复数据时,效率很好其他场景就一般,但是三路划分思路还是很价值的,有些快排思想变形体,要用划分去选数,他能保证跟key相等的数都排到中间去,三路划分的价值就体现出来了。

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/3e660177816b4516bbf5b7f2e52099c2.png

基准值确定的优化,使用rand函数,在区间中间随机找一个数据,比确定第一个数据要好很多,避免了一些极端情况

int randi = left + (rand() % (right - left + 1));  //  取随机数值  

示例图:
在这里插入图片描述

根据上图的三路划分思路以及示例图有如下代码:

void QuickSort(int* arr, int left, int right)   //   三路划分  
{if (left >= right){return;}int begin = left;int end = right;int randi = left + (rand() % (right - left + 1));  //  取随机数值作为基准值  swap(arr[randi], arr[left]);				//		把基准值放在最左边    int key = arr[left];					    //     定义key值    int cur = left + 1;   				//	这里类似于前后指针法  但是做了一些优化while (cur <= right)						//  左右同时往中间推  {											//  解除了中间数据相同影响性能的问题   if (arr[cur] < key)    //  遇到比key小的数值 交换数值  left++,cur++ {swap(arr[cur], arr[left]);left++;cur++;}else if (arr[cur] > key)   //  遇到比key大的数据  不管right此时为什么  直接交换{swap(arr[cur], arr[right]);right--;      }else{cur++;}}    //   每次都看cur指定的值  如果小于key就放左边 大于right就放右边  等于就继续走  //  left-right区间都是相同的值  不用进一步递归  QuickSort(arr, begin, left - 1);    //  左区间 QuickSort(arr, right + 1, end);   //   右区间  
}

三、内省排序

内省排序是基于直接插入排序,堆排序,快排实现的,在合适的情景使用合适的排序方式,使排序最优化,差不多和c++里面的sort排序的底层是一样的
内省排序可以认为不受数据分布的影响,无论什么原因划分不均匀,导致递归深度太深,他就是转换堆排了,堆排不受数据分布影响

内省排序要处理的就是递归的深度,递归层次太深的话,就转用堆排序,数据很少的话就直接使用直接插入排序,话不多说,直接上代码吧

void InsertSort(int* arr, int n)    //  直接插入排序
{for (int i = 0; i < n - 1; i++){int end = i;int tmp = arr[end + 1];while (end >= 0){if (arr[end] > tmp){arr[end + 1] = arr[end];end--;}else{break;}}arr[end + 1] = tmp;}
}void AdjustDown(int* arr, int parent, int n)   // 堆排序向下调整算法
{int child = parent * 2 + 1;while (child < n){if (child + 1 < n && arr[child] < arr[child + 1]){child++;}if (arr[child] > arr[parent]){swap(arr[child], arr[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}void HeapSort(int* arr, int n)     //  堆排
{for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(arr, i, n);}int end = n - 1;while (end > 0){swap(arr[0], arr[end]);AdjustDown(arr, 0, end);end--;}
}void IntroSort(int* arr, int left, int right, int depth, int defaltDepth)    //  内省排序  优化排序性能   保持稳定  n*logn
{if (left >= right){return;}if (right - left + 1 < 16)    //   区间大小比较小时   用插入排序  {InsertSort(arr + left, right - left + 1);return;}if (depth > defaltDepth)    //  当递归层次太深时   转用heap堆排序   {HeapSort(arr + left, right - left + 1);return;}depth++;int begin = left;int end = right;int randi = left + (rand() % (right - left + 1));    //  随机找基准值swap(arr[randi], arr[left]);int key = arr[left];int cur = left + 1;while (cur <= right){if (arr[cur] < key){swap(arr[cur], arr[left]);left++;cur++;}else if (arr[cur] > key){swap(arr[cur], arr[right]);right--;}else{cur++;}}IntroSort(arr, begin, left - 1, depth, defaltDepth);  //  递归左右部分  IntroSort(arr, right + 1, end, depth, defaltDepth);
}void QuickSort1(int* arr, int left, int right)  //   内省排序   对应数据对应处理办法  
{int depth = 0;int logn = 0;int n = right - left +1;for (int i = 1; i < n; i *= 2){logn++;           //  递归层数   }IntroSort(arr, left, right, depth, logn * 2);
}

代码涵盖了前面所学习的各种排序算法,插入,选择,交换都涉及到了
对于快排,从最开始的hoare版本,挖坑,前后指针,都有一些些小缺陷,到现在优化到三路快排,内省排序,把时间复杂度尽量调整到了 n*logn
为什么不直接用堆排呢?? 可能是想着多学一点知识吧 哈哈哈哈

四、排序算法复杂度以及稳定性的分析

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
相等的元素依然按照之前的相对顺序不发生改变就是稳定的

在这里插入图片描述
在这里插入图片描述

通过这几天的学习,已经把初阶数据结构的排序算法都学完了
冒泡是具有教学意义的存在
直接一点的选择和插入都是情理之中
带有gap的直接插入变成了希尔,让直男变的有情商
快排是虽然快,但是也有发挥不好的时候
堆和归并两兄弟是发挥一直很出色,速度也很快
稳定性高,而又快速的就属归并排序

总结

本篇博客下来,快排也能一直处于稳定的时间复杂度
想想内省排序,才是对症下药,给什么样的数据,用对应克制他的排序,根据需求解决问题
优化快排的同时,有对前面的排序知识有了更深刻的认知
排序的学习就到这里了,初阶数据结构也马上结束啦,下一篇博客小编将带着大家从头到尾过一遍初阶数据结构,不要走开,小编持续更新中~~~~~

会有点难走,但总归要坚持下去

在这里插入图片描述

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

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

相关文章

YOLOv9改进,YOLOv9引入CAS-ViT(卷积加自注意力视觉变压器)中AdditiveBlock模块,二次创新RepNCSPELAN4结构

摘要 CAS-ViT 是一种为高效移动应用设计的视觉Transformer。模型通过结合卷积操作与加性自注意机制,在保持高性能的同时显著减少计算开销,适合资源受限的设备如手机。其核心组件 AdditiveBlock 通过多维度信息交互和简化的加性相似函数,实现了高效的上下文信息整合,避免了…

【Leecode】Leecode刷题之路第62天之不同路径

题目出处 62-不同路径-题目出处 题目描述 个人解法 思路&#xff1a; todo代码示例&#xff1a;&#xff08;Java&#xff09; todo复杂度分析 todo官方解法 62-不同路径-官方解法 方法1&#xff1a;动态规划 思路&#xff1a; 代码示例&#xff1a;&#xff08;Java&…

PostgreSQL在Linux环境下的常用命令总结

标题 登录PgSQL库表基本操作命令新建库表修改库表修改数据库名称&#xff1a;修改表名称修改表字段信息 删除库表pgsql删除正在使用的数据库 须知&#xff1a; 以下所有命令我都在Linux环境中执行验证过&#xff0c;大家放心食用&#xff0c;其中的实际名称换成自己的实际名称即…

分布式协同 - 分布式锁一二事儿

文章目录 导图Pre概述概述1. 分布式互斥和临界资源的协调2. 分布式锁的基本原理3. 分布式锁的实现方式a. 基于数据库实现的分布式锁b. 基于Redis实现的分布式锁c. 基于Zookeeper实现的分布式锁 4. 高并发场景下的分布式锁优化a. 分段锁&#xff08;Sharded Locks&#xff09;b.…

FFmpeg 简介与编译

1. ffmpeg 简介&#xff1a; FFmpeg是一套可以用来记录、转换数字音频、视频&#xff0c;并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec&#xff0c;为了保证高可移…

Ubuntu-20.04安装 terminator

Ubuntu-20.04安装 terminator sudo apt install terminator 安装成功之后&#xff0c;在ubuntu终端里执行命令 terminator & terminator 窗口分割 基本操作 1. 创建新终端 水平分割&#xff1a;按下 Ctrl Shift E 创建一个新的水平分屏。 垂直分割&#xff1a;按下 Ct…

(已解决)wps无法加载此加载项程序mathpage.wll

今天&#xff0c;在安装Mathtype的时候遇到了点问题&#xff0c;如图所示 尝试了网上的方法&#xff0c;将C:\Users\Liai_\AppData\Roaming\Microsoft\Word\STARTUP路径中的替换为32位的Mathtype加载项。但此时&#xff0c;word又出现了问题 后来知道了&#xff0c;这是因为64位…

shell第二次作业

1. 使用case实现成绩优良差的判断 read -p "请输入你的成绩&#xff1a;" score if ! [[ "$score" ~ ^[0-9]$ ]];then echo "请输入数字" exit 1 fi if [ "$score" -lt 0 ] || [ "$score" -gt 100 ];then echo …

Appflyer记录卸载事件

Appflyer官方文档 1.原理 1.AppsFlyer每天向Firebase Cloud Messaging&#xff08;FCM&#xff09;和 Apple Push Notification Services&#xff08;APNS&#xff09;发送一次API请求。 2.然后FCM和APNS会发送一条静默推送消息&#xff0c;用于判断用户设备上是否仍装有相关应…

【81-90期】Java核心面试问题深度解析:性能优化与高并发设计

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;Java &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; 文章题目&#xff1a;Java核心面试问题深度解析&#xff1a;性能优化与高并发设计 摘要&#xff1a; 本文聚…

“移门缓冲支架:为家庭安全加码”

在智能家居日益普及的今天&#xff0c;科技不仅改变了我们的生活方式&#xff0c;也提升了家居的安全。移门缓冲支架作为一项结合了现代技术的小型装置&#xff0c;正逐渐成为提升家庭安全的重要配件。它通过吸收门关闭时的冲击力、减缓关门速度以及减少噪音等多重功能&#xf…

vue element-ui的el-image 和 el-table冲突层级冲突问题问题preview-teleported

问题: 解决代码:preview-teleported <el-image style"width: 50px; height: 50px" :src"props.row.url" :zoom-rate"1.2" :max-scale"7":min-scale"0.2" :preview-src-list"[props.row.url]" :initial-index&…

VR云展让企业实现产品的多样展示

随着科技的飞速进步&#xff0c;各行各业的企业正经历着前所未有的快速发展&#xff0c;企业形象的升级变得尤为重要。在此背景下&#xff0c;VR线上展厅以其独特的沉浸式3D体验脱颖而出&#xff0c;近年来赢得了广泛关注与认可。该模式已广泛应用于企业、展会机构、市政单位等…

强化安全责任意识,传音开展第四届信息及隐私安全文化宣传周活动

为了让信息及隐私安全责任意识深入每一位员工心中&#xff0c;传音连续多年开展信息及隐私安全文化宣传活动。11月4日至15日&#xff0c;传音控股第四届信息及隐私安全文化宣传周活动&#xff08;以下简称“传音安全周”&#xff09;在重庆、深圳、上海、南昌及海外多个国家地区…

人工智能的微积分基础

目录 ​编辑 引言 微积分的基本概念 1. 导数 2. 积分 3. 微分方程 微积分在人工智能中的应用 1. 机器学习中的优化 2. 反向传播算法 3. 概率与统计 4. 控制理论 5. 自然语言处理中的梯度 6. 计算机视觉中的积分 7. 优化算法中的微积分 8. 微分几何在深度学习中的…

canal同步数据教程

canal简介 官网&#xff1a;https://github.com/alibaba/canal 主要是基于 MySQL 数据库增量日志解析&#xff0c;提供增量数据订阅和消费&#xff0c;是一个实时同步的方案。 基于日志增量订阅和消费的业务包括 数据库镜像数据库实时备份索引构建和实时维护(拆分异构索引、…

《Learn Three.js》学习(3)光源

前言&#xff1a; WebGL本身不支持光源&#xff0c;不使用three.js,则需使用着色程序来模拟光源。 学习大纲&#xff1a; Three.js中的光源 特定光源的使用时机 如何调整和配置所有光源的行为 如何创建镜头光晕 光源表 基础光源&#xff1a;THRER.AmbientLight、THERE.Point…

shell(9)

声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&…

【头歌实训:递归实现斐波那契数列】

头歌实训&#xff1a;递归实现斐波那契数列 文章目录 任务描述相关知识递归相关知识递归举例何时使用递归定义是递归的数据结构是递归的问题的求解方法是递归的 编程要求测试说明源代码&#xff1a; 任务描述 本关任务&#xff1a;递归求解斐波那契数列。 相关知识 为了完成…

回声消除延时估计的一些方法

在音频信号处理&#xff0c;尤其是在回声消除和语音通信中&#xff0c;延时估计是一个至关重要的任务。回声消除技术旨在减少或消除在语音通信中由于信号反射而产生的回声。为了有效地实现这一点&#xff0c;系统需要准确估计发送信号和接收信号之间的延迟。通过了解延迟&#…