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

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

在这里插入图片描述

文章目录

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

前言

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

一、非递归实现快排

上篇博客基于递归实现了三个版本的快排,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的直接插入变成了希尔,让直男变的有情商
快排是虽然快,但是也有发挥不好的时候
堆和归并两兄弟是发挥一直很出色,速度也很快
稳定性高,而又快速的就属归并排序

总结

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

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

在这里插入图片描述

相关文章:

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

文章目录 前言一、非递归实现快排二、快排的优化版本三、内省排序四、排序算法复杂度以及稳定性的分析总结 前言 继上一篇博客基于递归的方式学习了快速排序和归并排序 今天我们来深究快速排序&#xff0c;使用栈的数据结构非递归实现快排&#xff0c;优化快排&#xff08;三路…...

C++的类功能整合

1. 类的基本概念 类是面向对象编程的核心&#xff0c;它封装了数据和操作数据的函数。 #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)方法&#xff1a;按照字典序比较 4.1.3 int compareTo(Strin…...

【docker】docker的起源与容器的由来、docker容器的隔离机制

Docker 的起源与容器的由来 1. 虚拟机的局限&#xff1a;容器的需求萌芽 在 Docker 出现之前&#xff0c;开发和部署软件主要依赖虚拟机&#xff08;VMs&#xff09;&#xff1a; 虚拟机通过模拟硬件运行操作系统&#xff0c;每个应用程序可以运行在自己的独立环境中。虽然虚…...

Window 安装 Nginx

参考链接 Windows 环境nginx安装使用及目录结构详解_windows 安装nginx-CSDN博客 Nginx 安装及配置教程&#xff08;Windows&#xff09;【安装】_nginx下载安装-CSDN博客 安装 1&#xff09;下载 nginx: download 2&#xff09;解压 3&#xff09;启动 3.1&#xff09;方…...

replace (regexp|substr, newSubstr|function)替换字符串中的指定部分

replace 方法用于替换字符串中的指定部分。它可以接受一个子字符串或正则表达式作为第一个参数&#xff0c;第二个参数是替换的内容。 用法示例 基本替换 let str "Hello, world!"; let newStr str.replace("world", "everyone"); console.lo…...

【ROS2】Ubuntu22.04安装ROS humble

一. ROS简介 1.1 什么是ROS ROS 是一个适用于机器人的开源的元操作系统。它提供了操作系统应有的服务&#xff0c;包括硬件抽象&#xff0c;底层设备控制&#xff0c;常用函数的实现&#xff0c;进程间消息传递&#xff0c;以及包管理。ROS的核心思想就是将机器人的软件功能做…...

cesium 3Dtiles变量

原本有一个变亮的属性luminanceAtZenith&#xff0c;但是新版本的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&#xff08;请自行安装并设置环境变量&#xff09; 2.将服务器上的WEARVER文件夹拷贝到开发环境下(其中要包含ecology和Resin目录) 3.通过idea创建一个基础Java项目,将jdk设置为1.8 4.添加依赖,需要将3个文件夹的所有jar包添加到项目中…...

【Stable Diffusion】安装教程

目录 一、python 安装教程 二、windows cuda安装教程 三、Stable Diffusion下载 四、Stable Diffusion部署&#xff08;重点&#xff09; 一、python 安装教程 &#xff08;1&#xff09;第一步下载 打开python下载页面&#xff0c;找到python3.10.9&#xff0c;点击右边…...

USB Type-C一线通扩展屏:多场景应用,重塑高效办公与极致娱乐体验

在追求高效与便捷的时代&#xff0c;启明智显USB Type-C一线通扩展屏方案正以其独特的优势&#xff0c;成为众多职场人士、娱乐爱好者和游戏玩家的首选。这款扩展屏不仅具备卓越的性能和广泛的兼容性&#xff0c;更能在多个应用场景中发挥出其独特的价值。 USB2.0显卡&#xff…...

【力扣】541.反转字符串2

问题描述 思路解析 每当字符达到2*k的时候&#xff0c;判断&#xff0c;同时若剩余字符>k,只对前k个进行判断&#xff08;这是重点&#xff09;因为字符串是不可变变量&#xff0c;所以将其转化为字符串数组&#xff0c;最后才将结果重新转变为字符串 字符串->字符数组 …...

什么是防抖与节流

防抖&#xff08;Debouncing&#xff09;与节流&#xff08;Throttling&#xff09; 在前端开发中&#xff0c;尤其是在处理用户输入、窗口调整大小、滚动事件等高频率触发的事件时&#xff0c;防抖和节流是两种常用的技术手段。它们可以帮助我们优化性能&#xff0c;减少不必…...

springboot vue 开源 会员收银系统 (12)购物车关联服务人员 订单计算提成

前言 完整版演示 http://120.26.95.195/ 开发版演示 http://120.26.95.195:8889/ 在之前的开发进程中&#xff0c;我们完成订单的挂单和取单功能&#xff0c;今天我们完成购物车关联服务人员&#xff0c;用户计算门店服务人员的提成。 1.商品关联服务人员 服务人员可以选择 一…...

FFmpeg 推流给 FreeSWITCH

FFmpeg 推流&#xff0c;貌似不难&#xff0c;网上有很多资料, 接到一个任务&#xff0c;推流给 FreeSWITCH&#xff0c;最开始以为很容易&#xff0c; 实则不然&#xff0c;FreeSWITCH uuid_debug_media <uuid>&#xff0c; 一直没人任何反应 仔细一查&#xff0c;Fr…...

.npmrc文件的用途

.npmrc 文件是 npm&#xff08;Node.js 的包管理工具&#xff09;用于配置项目或用户的设置文件。它可以存储与 npm 相关的配置信息&#xff0c;如注册表地址、认证信息、代理设置、安装路径等。.npmrc 文件可以出现在不同的地方&#xff0c;具有不同的作用范围&#xff0c;通常…...

C++游戏开发入门:如何从零开始实现自己的游戏项目?

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于C游戏开发的相关内容&#xff01; 关于【…...

Redis设计与实现第16章 -- Sentinel 总结1(初始化、主从服务器获取信息、发送信息、接收信息)

Sentinel是Redis的高可用解决方案&#xff1a;由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器&#xff0c;以及这些主服务器属下的所有从服务器&#xff0c;被监视的主服务器进入下线状态时&#xff0c;自动将下线主服务器属下的某个从服务器升级为新的主…...

Windows10+VirtualBox+Ubuntu:安装虚拟机VirtualBox,虚拟机中安装Ubuntu

一、需求 在Windows10系统中&#xff0c;安装虚拟机VirtualBox&#xff0c;VirtualBox中安装Ubuntu桌面版。 二、环境准备 系统环境 Windows10 内存&#xff1a;8G 虚拟化 虚拟机的运行&#xff0c;如果需要Windows系统开启虚拟化&#xff0c;可以通过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上微调和扩展大型语言模型&#xff08;LLM&#xff09;的指南。Torchtune 是一个PyTorch库&#xff0c;旨在让您轻松地…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

基于TurtleBot3在Gazebo地图实现机器人远程控制

1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

如何在Windows本机安装Python并确保与Python.NET兼容

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

SQL进阶之旅 Day 22:批处理与游标优化

【SQL进阶之旅 Day 22】批处理与游标优化 文章简述&#xff08;300字左右&#xff09; 在数据库开发中&#xff0c;面对大量数据的处理任务时&#xff0c;单条SQL语句往往无法满足性能需求。本篇文章聚焦“批处理与游标优化”&#xff0c;深入探讨如何通过批量操作和游标技术提…...

智警杯备赛--excel模块

数据透视与图表制作 创建步骤 创建 1.在Excel的插入或者数据标签页下找到数据透视表的按钮 2.将数据放进“请选择单元格区域“中&#xff0c;点击确定 这是最终结果&#xff0c;但是由于环境启不了&#xff0c;这里用的是自己的excel&#xff0c;真实的环境中的excel根据实训…...

零基础在实践中学习网络安全-皮卡丘靶场(第十一期-目录遍历模块)

经过前面几期的内容我们学习了很多网络安全的知识&#xff0c;而这期内容就涉及到了前面的第六期-RCE模块&#xff0c;第七期-File inclusion模块&#xff0c;第八期-Unsafe Filedownload模块。 什么是"遍历"呢&#xff1a;对学过一些开发语言的朋友来说应该知道&…...