数据结构——排序第三幕(深究快排(非递归实现)、快排的优化、内省排序,排序总结)超详细!!!!
文章目录
- 前言
- 一、非递归实现快排
- 二、快排的优化版本
- 三、内省排序
- 四、排序算法复杂度以及稳定性的分析
- 总结
前言
继上一篇博客基于递归的方式学习了快速排序和归并排序
今天我们来深究快速排序,使用栈的数据结构非递归实现快排,优化快排(三路划分)
干货满满,上车
一、非递归实现快排
上篇博客基于递归实现了三个版本的快排,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相等的数都排到中间去,三路划分的价值就体现出来了。
基准值确定的优化,使用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的直接插入变成了希尔,让直男变的有情商
快排是虽然快,但是也有发挥不好的时候
堆和归并两兄弟是发挥一直很出色,速度也很快
稳定性高,而又快速的就属归并排序
总结
本篇博客下来,快排也能一直处于稳定的时间复杂度
想想内省排序,才是对症下药,给什么样的数据,用对应克制他的排序,根据需求解决问题
优化快排的同时,有对前面的排序知识有了更深刻的认知
排序的学习就到这里了,初阶数据结构也马上结束啦,下一篇博客小编将带着大家从头到尾过一遍初阶数据结构,不要走开,小编持续更新中~~~~~
会有点难走,但总归要坚持下去
相关文章:
数据结构——排序第三幕(深究快排(非递归实现)、快排的优化、内省排序,排序总结)超详细!!!!
文章目录 前言一、非递归实现快排二、快排的优化版本三、内省排序四、排序算法复杂度以及稳定性的分析总结 前言 继上一篇博客基于递归的方式学习了快速排序和归并排序 今天我们来深究快速排序,使用栈的数据结构非递归实现快排,优化快排(三路…...
C++的类功能整合
1. 类的基本概念 类是面向对象编程的核心,它封装了数据和操作数据的函数。 #include <iostream> using namespace std;class MyClass { public:int publicData;void publicFunction() {cout << "Public function" << endl;}private:i…...
《String类》
目录 一、定义与概述 二、创建字符串对象 2.1 直接赋值 2.2 使用构造函数 三、字符串的不可变性 四、常用方法 4.1 String对象的比较 4.1.1 比较是否引用同一个对象 4.1.2 boolean equals(Object anObject)方法:按照字典序比较 4.1.3 int compareTo(Strin…...
【docker】docker的起源与容器的由来、docker容器的隔离机制
Docker 的起源与容器的由来 1. 虚拟机的局限:容器的需求萌芽 在 Docker 出现之前,开发和部署软件主要依赖虚拟机(VMs): 虚拟机通过模拟硬件运行操作系统,每个应用程序可以运行在自己的独立环境中。虽然虚…...
Window 安装 Nginx
参考链接 Windows 环境nginx安装使用及目录结构详解_windows 安装nginx-CSDN博客 Nginx 安装及配置教程(Windows)【安装】_nginx下载安装-CSDN博客 安装 1)下载 nginx: download 2)解压 3)启动 3.1)方…...
replace (regexp|substr, newSubstr|function)替换字符串中的指定部分
replace 方法用于替换字符串中的指定部分。它可以接受一个子字符串或正则表达式作为第一个参数,第二个参数是替换的内容。 用法示例 基本替换 let str "Hello, world!"; let newStr str.replace("world", "everyone"); console.lo…...
【ROS2】Ubuntu22.04安装ROS humble
一. ROS简介 1.1 什么是ROS ROS 是一个适用于机器人的开源的元操作系统。它提供了操作系统应有的服务,包括硬件抽象,底层设备控制,常用函数的实现,进程间消息传递,以及包管理。ROS的核心思想就是将机器人的软件功能做…...
cesium 3Dtiles变量
原本有一个变亮的属性luminanceAtZenith,但是新版本的cesium没有这个属性了。于是 let lightColor 3.0result._customShader new this.ffCesium.Cesium.CustomShader({fragmentShaderText:void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial mate…...
配置泛微e9后端开发环境
配置泛微e9的后端开发环境 1.安装jdk1.8(请自行安装并设置环境变量) 2.将服务器上的WEARVER文件夹拷贝到开发环境下(其中要包含ecology和Resin目录) 3.通过idea创建一个基础Java项目,将jdk设置为1.8 4.添加依赖,需要将3个文件夹的所有jar包添加到项目中…...
【Stable Diffusion】安装教程
目录 一、python 安装教程 二、windows cuda安装教程 三、Stable Diffusion下载 四、Stable Diffusion部署(重点) 一、python 安装教程 (1)第一步下载 打开python下载页面,找到python3.10.9,点击右边…...
USB Type-C一线通扩展屏:多场景应用,重塑高效办公与极致娱乐体验
在追求高效与便捷的时代,启明智显USB Type-C一线通扩展屏方案正以其独特的优势,成为众多职场人士、娱乐爱好者和游戏玩家的首选。这款扩展屏不仅具备卓越的性能和广泛的兼容性,更能在多个应用场景中发挥出其独特的价值。 USB2.0显卡ÿ…...
【力扣】541.反转字符串2
问题描述 思路解析 每当字符达到2*k的时候,判断,同时若剩余字符>k,只对前k个进行判断(这是重点)因为字符串是不可变变量,所以将其转化为字符串数组,最后才将结果重新转变为字符串 字符串->字符数组 …...
什么是防抖与节流
防抖(Debouncing)与节流(Throttling) 在前端开发中,尤其是在处理用户输入、窗口调整大小、滚动事件等高频率触发的事件时,防抖和节流是两种常用的技术手段。它们可以帮助我们优化性能,减少不必…...
springboot vue 开源 会员收银系统 (12)购物车关联服务人员 订单计算提成
前言 完整版演示 http://120.26.95.195/ 开发版演示 http://120.26.95.195:8889/ 在之前的开发进程中,我们完成订单的挂单和取单功能,今天我们完成购物车关联服务人员,用户计算门店服务人员的提成。 1.商品关联服务人员 服务人员可以选择 一…...
FFmpeg 推流给 FreeSWITCH
FFmpeg 推流,貌似不难,网上有很多资料, 接到一个任务,推流给 FreeSWITCH,最开始以为很容易, 实则不然,FreeSWITCH uuid_debug_media <uuid>, 一直没人任何反应 仔细一查,Fr…...
.npmrc文件的用途
.npmrc 文件是 npm(Node.js 的包管理工具)用于配置项目或用户的设置文件。它可以存储与 npm 相关的配置信息,如注册表地址、认证信息、代理设置、安装路径等。.npmrc 文件可以出现在不同的地方,具有不同的作用范围,通常…...
C++游戏开发入门:如何从零开始实现自己的游戏项目?
成长路上不孤单😊😊😊😊😊😊 【14后😊///C爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于C游戏开发的相关内容! 关于【…...
Redis设计与实现第16章 -- Sentinel 总结1(初始化、主从服务器获取信息、发送信息、接收信息)
Sentinel是Redis的高可用解决方案:由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主…...
Windows10+VirtualBox+Ubuntu:安装虚拟机VirtualBox,虚拟机中安装Ubuntu
一、需求 在Windows10系统中,安装虚拟机VirtualBox,VirtualBox中安装Ubuntu桌面版。 二、环境准备 系统环境 Windows10 内存:8G 虚拟化 虚拟机的运行,如果需要Windows系统开启虚拟化,可以通过BIOS设置。 “虚拟…...
Torchtune在AMD GPU上的使用指南:利用多GPU能力进行LLM微调与扩展
Torchtune on AMD GPUs How-To Guide: Fine-tuning and Scaling LLMs with Multi-GPU Power — ROCm Blogs 这篇博客提供了一份详细的使用Torchtune在AMD GPU上微调和扩展大型语言模型(LLM)的指南。Torchtune 是一个PyTorch库,旨在让您轻松地…...
C底层 函数栈帧
文章目录 一,什么是寄存器 二,栈和帧 前言 我们在学习c语言程序的时候,是不是有很多的疑问,如 1,为什么形参不可以改变实参 2,为什么我们编写程序的时候会出现烫烫烫......这个乱码 3,那些局…...
【模块一】kubernetes容器编排进阶业务容器化案例
Kubernetes 实战案例 Kubernetes实战案例-规划(基于nerdctl buildkitdcontainerd构建容器镜像) 业务容器化优势: ① 提高资源利用率、节约部署IT成本。 ② 提高部署效率,基于kubernetes实现微服务的快速部署与交付、容器的批量调度与秒级启动。 ③…...
可视化建模以及UML期末复习篇----相关软件安装
作为一个过来人,我的建议是别过来。 一、可视化建模 <1>定义: 官方:一种使用图形符号来表示系统结构和行为的建模技术。 我:其实说白了就是把工作流程用图形画出来。懂不? <2>作用: 提高理解和分析复杂系统的能力。促…...
Appflyer记录卸载事件
Appflyer官方文档 1.原理 1.AppsFlyer每天向Firebase Cloud Messaging(FCM)和 Apple Push Notification Services(APNS)发送一次API请求。 2.然后FCM和APNS会发送一条静默推送消息,用于判断用户设备上是否仍装有相关应…...
JDK17 AbstractQueuedSynchronizer 二 条件队列
条件队列 同步队列中的线程是为了争抢锁,而条件队列中的线程是主动释放锁,挂起自己,等条件满足时被别的线程唤醒,继续工作。 AQS里只有1个同步队列,但可以有多个等待队列,每个等待队列对应一个ConditionO…...
8 设计模式之简单工厂模式
设计模式是软件开发中的一套通用解决方案,而简单工厂模式则是最基础、最常用的一种创建型模式。在这篇博客中,我将为大家详细介绍简单工厂模式的概念、优缺点,以及通过一个饮料制作的案例,帮助大家更好地理解和应用这种模式。 一、…...
计算机的错误计算(一百六十九)
摘要 探讨 MATLAB 中一个不动点的计算精度问题。 不动点是一类特殊的循环迭代。它有形式 例1. 已知迭代[1] 计算 显然,每个 均为 0.5 . 下面看看 MATLAB 的计算结果。不妨不用循环语句,直接用算术表达式表示 这时计算结果在如下图片: …...
Android 图形系统之三:SurfaceControl
在 Android 系统中,SurfaceControl 是一个关键的类,用于管理应用窗口和屏幕上的显示内容。它与 SurfaceFlinger 紧密交互,通过 BufferQueue 提供高效的图形缓冲区管理能力。SurfaceControl 是 Android 的显示架构中不可或缺的部分,…...
Laravel8.5+微信小程序实现京东商城秒杀方案
一、商品秒杀涉及的知识点 鉴权策略封装掊口访问频次限制小程序设计页面防抖接口调用订单创建事务使用超卖防御 二、订单库存系统方案(3种) 下单减库存 优点是库存和订单的强一致性,商品不会卖超,但是可能导致恶意下单ÿ…...
Makefile 入门指南:构建自动化编译流程
个人主页:chian-ocean 文章专栏 前言 make 和 Makefile 是编译和构建软件项目时非常常用的工具和文件,它们通常配合使用来自动化项目的编译过程。 make 定义:make 是一个构建自动化工具,用于根据项目文件的依赖关系自动完成编译…...
广州学校网站建设/关键词怎样做优化排名
题库来源:安全生产模拟考试一点通公众号小程序 2021年A证(安全员)考试总结及A证(安全员)复审模拟考试,包含A证(安全员)考试总结答案和解析及A证(安全员)复审模拟考试练习。由安全生产模拟考试一点通公众号结合国家A证(安全员)考试最新大纲及A证(安全员)…...
网页制作工具有什么/百度视频排名优化
C一、选择题1、 若已经定义“struct stu {int a, b;} student;”,则下列输入语句中正确的是D)scanf(“%d”,&student.a);2、 若已有以下结构体定义,则值为2的表达式是A)c[0].y;struct cmplx{int x;int y;}c[]{1,2,…...
手机wordpress上传失败/网站制作费用
常见web框架中Struts2和SpringMVC独占鳌头,SpringMVC和Struts有什么不同? 我们可以从各个方面进行对比: 一:框架的思想设计上 SpringMVC控制器是基于方法上拦截,是单例的. Struts2控制器是基于类上拦截,是多例的,多例会带来一定内存消耗. 二:配置文件上执行流程 Struts2是通过…...
北京网站建设策划方案/网络整合营销案例
何志丹 例子和习题下载 http://download.csdn.net/detail/he_zhidan/9656738...
建了网站怎么装饰/国内可访问的海外网站和应用
转自 https://www.cnblogs.com/wuchanming/p/4339770.html 转载于:https://www.cnblogs.com/yi-mu-xi/p/11031493.html...
怎样会展网站建设/衡水seo培训
实验要求:按照上图配置接口地址以及OSPF,使两台路由器互相学习到对方的Loopback地址,使出现在路由表中对方的Loopback地址的掩码为 /8具体配置:R1:interface Loopback0ip address 1.1.1.1 255.0.0.0ip ospf network point-to-poin…...