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

DS几大常见排序讲解和实现(下)(15)

文章目录

  • 前言
  • 一、快排的思想
  • 二、Hoare版
    • 基本思路
    • 代码实现
  • 三、挖坑法
    • 基本思路
    • 代码实现
  • 四、双指针法
    • 基本思想
    • 代码实现
  • 五、三数取中
  • 六、小区间优化
  • 七、三路划分
  • 八、自省排序
  • 总结


前言

  其实下篇就单独讲个快速排序
  你可能会想这是什么神通,竟然能单独开一篇来讲解,请往下看!


一、快排的思想

  无论是什么版本的快排,都是遵循一个原则:以升序为例,选 key划分,选取一个 key ,使 key 左边的值小于等于 key ,右边的值大于 key ,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止,这是快排的核心思想,由此一共有以下几种变式

二、Hoare版

基本思路

  选取最左边的值为 key,比较时右边先走,因选的是左边,所以右边会先走(向左走),当右边在走的过程中遇到小于等于 key 的值时停下,右边走完后,换左边走(向右走),当遇到大于 key 的值时停止,此时交换左右两处的值,当左遇到右时(必定相遇,因为一次走一步),终止循环,执行最后一步,交换此时左(右)与 key 值,此时就完成了需求:右边值 <= key < 左边值

  完成这一单次排序后,将排序这个大问题转成小问题,指定 begin 与 end ,此时 [begin,key - 1]、key、[key + 1,end],将其中的两块区域传入函数,继续执行递归,当所有数据都排序完成后,快排就结束了

  如果 key 选在最左边,那么右边先走;如果 key 选在最右边,左边先走。这样做的目的是保证最后一次交换时(左右重叠时与 key 值的交换)key 左边的数小于等于 key,原因如下:
在这里插入图片描述

动图如下:
在这里插入图片描述
递归展开图如下:
在这里插入图片描述

代码实现

int Partition(int* arr, int left, int right)
{int begin = left, end = right;int key = arr[left];while (begin < end){// 找小// 不加等于,若左右指针指向同一个值,会陷入死循环while (begin < end && arr[end] >= key){ // 等于是必要的,不然可能会陷入死循环end--;}// 找大while (begin < end && arr[begin] <= key){begin++;}Swap(&arr[begin], &arr[end]);}Swap(&arr[begin], &arr[left]);return begin;
}void QuickSort(int* arr, int left, int right){if (left >= right){return ;}int KeyIndex = Partition(arr, left, right);QuickSort(arr, left, KeyIndex - 1);QuickSort(arr, KeyIndex + 1, right);
}

三、挖坑法

基本思路

  挖坑法核心思想与霍尔版一致,不过挖坑法为了便于理解,引入了坑位这个概念,简单来说就是先在 key 处挖坑,然后右边先走(假设 key 在最左边),找到小于等于 key 的值,就将此值放入到坑中,并在这里形成新坑;然后是左边走,同样的,找到值 -> 填入坑 -> 挖新坑,如此重复,直到左右相遇,此时的相遇点必然是一个未填充的坑,当然这个坑就是给 key 值准备的

动图如下:

在这里插入图片描述

代码实现

void QuickSort(int* arr, int left, int right)
{if (left >= right){return ;}int index = GetMidIndex(arr, left, right); // 三数取中,我们后面会讲Swap(&arr[index], &arr[left]);int begin = left, end = right;int pivot = begin;int key = arr[begin];while (begin < end){// 右边找小,放到左边while (begin < end && arr[end] >= key){end--;}// 小的放到左边的坑里,自己形成新的坑位arr[pivot] = arr[end];pivot = end;// 左边找大while (begin < end && arr[begin] <= key){begin++;}// 大的放到左边的坑里,自己形成新的坑位arr[pivot] = arr[begin];pivot = begin;}pivot = begin;arr[pivot] = key;QuickSort(arr, left, pivot - 1);QuickSort(arr, pivot + 1, right);
}

四、双指针法

基本思想

  双指针的实现方式与上面两种截然不同,但最核心的思想仍是依赖 key,一样的先找 key,然后定义两个指针 prev 与 cur ,prev 的起始位置为 key ,而 cur 则是位于 prev 的下一位,判断 cur 处的值是否小于等于 key 值,如果是,则先 ++prev 后再交换 prev 与 cur 处的值,如此循环,直到 cur 移动至数据尾,最后一次交换为 key 与 prev 间的交换,交换完成后,就达到了快排的要求

在这里插入图片描述

这个方法也是我最喜欢的解法,很漂亮

代码实现

int Partition(int* arr, int left, int right)
{int key = left;int prev = left, cur = left + 1;while (cur <= right){if (arr[cur] < arr[key] && ++prev != cur){ // 加个判断,防止无意义交换Swap(&arr[cur], &arr[prev]);}cur++;}Swap(&arr[prev], &arr[key]);return prev;
}void QuickSort(int* arr, int left, int right)
{if (left >= right){return ;}int index = Partition(arr, left, right);QuickSort(arr, left, index - 1);QuickSort(arr, index + 1, right);
}

五、三数取中

  前面说过,接近有序或逆序的数据,对于快排是不太友好的,因为未优化前的快排选 key 始终是最右或最左,即有可能是最大或最小数,就像二分取中一样,快排只有尽可能取到中间数,才能发挥它的最大实力

  因此我们可以借助一个函数:三数取中,分别取数据头、尾、中间进行比较,选取其中位于中间的数,再将其交换至数据首位(待会 key 取右边),经过这一优化后,快排的提升是非常明显的

int GetMidIndex(const int* arr, int left, int right)
{int mid = (left + right) >> 1; // 二进制向右移动一位if (arr[left] < arr[mid]){ // arr[left] < arr[mid]if (arr[mid] < arr[right]){return mid;}else if (arr[left] > arr[right]){return left;}else {return right;}}else { // arr[left] > arr[mid]if (arr[mid] > arr[right]){return mid;}else if (arr[left] < arr[right]){return left;}else {return right;}}
}

六、小区间优化

  对于递归来说,越是接近小区间,所耗费时间就越长,越不利于排序,此时坚持使用快排是个不明智的选择,为此我们可以借助其他排序,弥补快排在小区间排序中的不足,就像二叉递归树到最后几层节点最多,其实这很浪费栈帧,很不好

  这里借助的是直接插入排序,直接插入排序是个很不错的排序,稳定、速度也是中规中矩,小区间的定义取决于我们,我这里是将小于20的区间定义为小区间

void QuickSort(int* pa, int begin, int end)
{assert(pa);//思路:选出key,key 的右边小于key,key 的左边大于keyif (begin >= end)return;if ((end - begin + 1) < 20)InsertSort(pa + begin, (end - begin + 1));	//这就是小区间优化QuickSort(pa, begin, keyi - 1);QuickSort(pa, keyi + 1, end);
}

七、三路划分

力扣912.排序数组
在这里插入图片描述
这题蛮逆天的,官方给的快排竟然过不了,应该是题出得早,但是后面偷偷加了测试样例,原先的快排速度不足,就过不了了,我们看下报错样例,方法是官方给的快慢指针快排法
在这里插入图片描述
我们发现这个测试样例有一个特点,就是同一值的数据特别多,而我们的快排一趟排序只能选出一个数,对于别的一样的值却没有什么特别的处理方式,可以放左边,也可以放在右边

这个时候,有一个优化方式,就是三路划分,顾名思义,每趟排序将原数组分成三组,左边是小于key的,中间是等于key的,右边是大于key的,如图所示:
在这里插入图片描述

三路划分的核心在于控制中路的左右边界,这里需要借助三个变量:left、right、cur,显然 left 位于 begin 处,right 位于 end 处,cur 位于 begin + 1 处。实现起来也很简单

  1. key默认取left位置的值
  2. left指向区间最左边,right指向区间最后边,cur指向 left + 1 位置
  3. cur遇到比key小的值后跟left位置交换,换到左边,left++,cur++
  4. cur遇到比key大的值后跟right位置交换,换到右边,right–(注意cur并不需要自加!!!)
  5. cur遇到跟key相等的值后,cur++
  6. 直到 cur > right 结束

代码?自己去实现吧~

八、自省排序

  其实这也是一个优化,哈哈想不到吧!还能继续优化~
  前面说过,递归太深了,开栈帧也是一个不小的消耗,主排序仍然是快排,当我们的深度超过了logN的时候,则改用堆排序

如果求logN:
在这里插入图片描述


总结

  接下来会出一篇快排和归排的非递归版本,我们的排序篇基本上也就结束了~

相关文章:

DS几大常见排序讲解和实现(下)(15)

文章目录 前言一、快排的思想二、Hoare版基本思路代码实现 三、挖坑法基本思路代码实现 四、双指针法基本思想代码实现 五、三数取中六、小区间优化七、三路划分八、自省排序总结 前言 其实下篇就单独讲个快速排序   你可能会想这是什么神通&#xff0c;竟然能单独开一篇来讲…...

电脑视频剪辑大比拼,谁更胜一筹?

随着短视频的火爆&#xff0c;越来越多的人开始尝试自己动手制作视频&#xff0c;无论是记录生活点滴还是创作个性短片&#xff0c;一款好用的视频剪辑软件是必不可少的。今天&#xff0c;我们就从短视频运营的角度&#xff0c;来聊聊几款热门的电脑视频剪辑软件&#xff0c;看…...

计算机毕业设计 基于Web的景区管理系统的设计与实现 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…...

计算生物学与生物信息学漫谈-2-测序深度/读长质量和Fasta处理

上一篇文章中我们介绍了测序技术的由来与发展&#xff0c;那么在介绍第三代测序的时候&#xff0c;我们提到了关于测序深度和读长的问题&#xff0c;那么本篇文章就详解介绍一下。 计算生物学与生物信息学漫谈-1-测序一路走来-CSDN博客 目录 1.测序深度SEQUENCING DEPTH &…...

基于SSM+微信小程序的电子点餐管理系统(点餐1)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于SSM微信小程序的电子点餐管理系统实现了管理员及用户。管理员实现了首页、个人中心、餐品分类管理、特色餐品管理、订单信息管理、用户管理、特价餐品管理、活动订单管理、系统管理。…...

IO进程---day5

1、使用有名管道实现两个进程之间的相互通信 //管道文件 #include<myhead.h> int main(int argc, const char *argv[]) {//创建有名管道文件1if(mkfifo("./pipe1",0664)-1){perror("创建管道文件失败");return 0;}if(mkfifo("./pipe2",066…...

ROS理论与实践学习笔记——5 ROS机器人系统仿真之URDF(Unified Robot Description Format)语法详解

URDF 文件是一个标准的 XML 文件格式&#xff0c;用于在 ROS 中描述机器人模型的结构。URDF 通过预定义的一系列标签&#xff0c;简洁地表达机器人的组成和运动关系。虽然机器人模型可能非常复杂&#xff0c;但在 URDF 中可以主要简化为两个核心部分&#xff1a; 连杆&#xff…...

常见SQL注入攻击示例与原理及其防御措施

SQL 注入&#xff08;SQL Injection&#xff09;是一种代码注入技术&#xff0c;用于攻击数据驱动的应用程序&#xff0c;主要通过在输入字段或 URL 查询参数中插入恶意 SQL 语句来实现。攻击者利用应用程序对用户输入数据的未充分验证或过滤&#xff0c;将恶意 SQL 语句注入到…...

Node.js 中的 WebSocket 底层实现

WebSockets 是一种网络通信协议&#xff0c;可实现双向客户端-服务器通信。 WebSockets 通常用于需要即时更新的应用程序&#xff0c;使用 HTTP 之上的持久双工通道来支持实时交互&#xff0c;而无需持续进行连接协商。服务器推送是 WebSockets 的众多常见用例之一。 本文首先…...

MySQl数据库的基本操作

1.1创建数据库 使用CREATE DATABASE语句可以轻松创建MySQL数据库&#xff0c;语法如下&#xff1a; CREATE DATABASE 数据库名; 例&#xff1a;创建fruitsales数据库 CREATE DATABASE fruitsales;1.2 查看数据库 使用SHOW语句查看当前服务器下所有已经存在的数据库 SHOW DAT…...

Egg.js 项目的合理 ESLint 配置文件模板

Egg.js 项目的合理 ESLint 配置文件模板 安装依赖 npm install eslint babel/eslint-parser eslint-plugin-import eslint-plugin-promise eslint-plugin-node --save-dev extends: 扩展了 eslint-config-egg 以及其他一些常用的插件配置。 parser: 使用 babel/eslint-parse…...

算法专题七: 分治归并

目录 1. 排序数组2. 交易逆序对的总数3. 计算右侧小于当前元素的个数4. 翻转对 1. 排序数组 算法思路: 本道题使用归并的思路进行排序, 先讲数组分为左右两个区间, 然后合并两个有序数组. class Solution {vector<int> tmp; public:vector<int> sortArray(vector&…...

一个基于vue功能强大的表格组件--vxe-table的二次封装

基础使用 一个基于 vue 的 PC 端表格组件&#xff0c;支持增删改查、虚拟滚动、懒加载、快捷菜单、数据校验、树形结构、打印导出、表单渲染、数据分页、虚拟列表、模态窗口、自定义模板、渲染器、贼灵活的配置项、扩展接口等… <vxe-grid v-bind"gridOptions1"…...

CSS网页布局(重塑网页布局)

一、实现两列布局 许多网站有一些特点&#xff0c;如页面顶部放置一个大的导航或广告条&#xff0c;右侧是链接或图片&#xff0c;左侧放置主要内容&#xff0c;页面底部放置版权信息等。 一般情况&#xff0c;此类网页布局的两列都有固定的宽度&#xff0c;而且从内容上很容易…...

计算机网络:数据链路层 —— 以太网(Ethernet)

文章目录 局域网局域网的主要特征 以太网以太网的发展100BASE-T 以太网物理层标准 吉比特以太网载波延伸物理层标准 10吉比特以太网汇聚层交换机物理层标准 40/100吉比特以太网传输媒体 局域网 局域网&#xff08;Local Area Network, LAN&#xff09;是一种计算机网络&#x…...

考研前所学c语言02(2024/10/16)

1.一个十进制的数转化为二进制的就是不断除二取余&#xff0c;得到的余数从下到上取 比如123&#xff1a; 结果为&#xff1a; 同理其他的十进制转八进制&#xff0c;十六进制就除八&#xff0c;除十六即可 再比如123转十六进制&#xff1a; 因为余数是11&#xff0c;十六进…...

R语言绘图——坐标轴及图例

掌握坐标轴与图例的设置与调整&#xff0c;对于提升数据可视化的清晰度和可读性至关重要。通过这些工具&#xff0c;可以有效地传达数据背后的故事&#xff0c;提高图表的表现力。 0x01 坐标轴 一、坐标轴的设置 1、修改坐标轴的标签 在ggplot2中&#xff0c;坐标轴是根据数…...

JDK中socket源码解析

目录 1、Java.net包 1. Socket通信相关类 2. URL和URI处理类 3. 网络地址和主机名解析类 4. 代理和认证相关类 5. 网络缓存和Cookie管理类 6. 其他网络相关工具类 2、什么是socket&#xff1f; 3、JDK中socket核心Api 4、核心源码 1、核心方法 2、本地方法 3、lin…...

Ansible自动化运维项目实战指南

Ansible自动化运维项目实战指南 在当今快速发展的IT环境中&#xff0c;运维工作的复杂性和规模性日益增加&#xff0c;传统的手动运维方式已难以满足高效、可靠、可重复性的需求。Ansible作为一款开源的自动化运维工具&#xff0c;凭借其简单易用、无需代理、基于SSH的架构特性…...

MySQL【知识改变命运】10

联合查询 0.前言1.联合查询在MySQL里面的原理2.练习一个完整的联合查询2.1.构造练习案例数据2.2 案例&#xff1a;⼀个完整的联合查询的过程2.2.1. 确定参与查询的表&#xff0c;学⽣表和班级表2.2.2. 确定连接条件&#xff0c;student表中的class_id与class表中id列的值相等2.…...

Vue记事本应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展&#xff1a;显示创建时间8. 功能扩展&#xff1a;记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

【生成模型】视频生成论文调研

工作清单 上游应用方向&#xff1a;控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

宇树科技,改名了!

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