当前位置: 首页 > 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;旨在让您轻松地…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

离线语音识别方案分析

随着人工智能技术的不断发展&#xff0c;语音识别技术也得到了广泛的应用&#xff0c;从智能家居到车载系统&#xff0c;语音识别正在改变我们与设备的交互方式。尤其是离线语音识别&#xff0c;由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力&#xff0c;广…...

图解JavaScript原型:原型链及其分析 | JavaScript图解

​​ 忽略该图的细节&#xff08;如内存地址值没有用二进制&#xff09; 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么&#xff1a;保存在堆中一块区域&#xff0c;同时在栈中有一块区域保存其在堆中的地址&#xff08;也就是我们通常说的该变量指向谁&…...

车载诊断架构 --- ZEVonUDS(J1979-3)简介第一篇

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…...

rm视觉学习1-自瞄部分

首先先感谢中南大学的开源&#xff0c;提供了很全面的思路&#xff0c;减少了很多基础性的开发研究 我看的阅读的是中南大学FYT战队开源视觉代码 链接&#xff1a;https://github.com/CSU-FYT-Vision/FYT2024_vision.git 1.框架&#xff1a; 代码框架结构&#xff1a;readme有…...