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

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

Xen Server服务器释放磁盘空间

disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...

宇树科技,改名了!

提到国内具身智能和机器人领域的代表企业&#xff0c;那宇树科技&#xff08;Unitree&#xff09;必须名列其榜。 最近&#xff0c;宇树科技的一项新变动消息在业界引发了不少关注和讨论&#xff0c;即&#xff1a; 宇树向其合作伙伴发布了一封公司名称变更函称&#xff0c;因…...