【数据结构】—交换排序之快速排序究极详解,手把手带你从简单的冒泡排序升级到排序的难点{快速排序}(含C语言实现)
食用指南:本文在有C基础的情况下食用更佳
🔥这就不得不推荐此专栏了:C语言
♈️今日夜电波:靴の花火—ヨルシカ
0:28━━━━━━️💟──────── 5:03
🔄 ◀️ ⏸ ▶️ ☰
💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍
目录
♉️一、前置知识—什么是交换排序
♊️二、冒泡排序
冒泡排序的思想
冒泡排序的实现
♋️三、快速排序 (排序算法中的重点,肥肠重要!!!)
快速排序的思想
快速排序的三种版本实现
1、hoare版本(基础版本)
♒️快排的优化
2、 挖坑法
3、前后指针法
♉️一、前置知识—什么是交换排序
交换排序的基本思想是通过比较相邻元素的大小关系,如果两个相邻元素的大小关系不满足排序要求,就交换它们的位置,以达到排序的目的。交换排序分为两种,即冒泡排序和快速排序。
♊️二、冒泡排序
冒泡排序的思想
冒泡排序是一种基本的排序算法,它的思想是将待排序的元素依次比较相邻的两个元素,根据比较结果交换它们的位置,从而使较大(或较小)的元素逐渐往后移动,最终实现整个序列的排序。
具体的实现过程如下:
1. 从序列的第一个元素开始,依次比较相邻的两个元素的大小。
2. 如果前一个元素比后一个元素大(或小),则交换它们的位置。
3. 继续比较序列中下一个相邻的元素,直至最后一个元素。
4. 重复以上操作,每一轮比较都将序列中最大(或最小)的元素排到了序列的末尾。
5. 经过多轮比较后,序列中的元素就按照从小到大(或从大到小)的顺序排好了。
一图理解~
冒泡排序的实现
太简单了,就不多解释了,看代码即可
void BubbleSort(int* a, int n)
{for (int j = 0; j < n; ++j){bool exchange = false;for (int i = 1; i < n-j; i++){if (a[i - 1] > a[i]){int tmp = a[i];a[i] = a[i - 1];a[i - 1] = tmp;exchange = true;}}if (exchange == false){break;}}
}
♋️三、快速排序 (排序算法中的重点,肥肠重要!!!)
快速排序的思想
快速排序的基本思想是选定一个轴值,将待排序序列划分为两部分,一部分是小于轴值的元素,一部分是大于轴值的元素。然后对于这两部分分别递归地进行快速排序,直到排序完成。具体实现时,可以选择待排序序列的第一个元素作为轴值(通常需要进行比较选出,后面会详讲),也可以随机选择一个元素作为轴值,然后将序列中的元素与轴值进行比较,小于轴值的元素放在轴值的左边,大于轴值的元素放在轴值的右边,相等的元素可以放在任 一 一边,最后将左右两部分序列递归地进行快速排序即可。快速排序一般有三种实现方法:hoare法、挖坑法、前后指针法。
一图先有个大概的了解~
以下的内容是循序渐进的,建议大家一步一步往下看!
快速排序的三种版本实现
1、hoare版本(基础版本)
上来请先看一张图~
具体实现步骤如下:
选择一个基准元素 key,通常可以选择第一个或者最后一个元素作为基准元素(这里的key值选第一个)。
定义两个指针 left和 right 分别指向数组的开头和结尾。
从右向左扫描数组,如果当前元素大于或等于基准元素,则指针 right 向左移动一位。
从左向右扫描数组,如果当前元素小于或等于基准元素,则指针 left 向右移动一位。
如果 left < right,则交换指针所指向的元素。
当 left >= right 时,说明当前分区操作已经完成,交换基准元素和 left 指针指向的元素,并返回 left 的值。
对基准元素左侧的子数组和右侧的子数组分别递归执行上述步骤,直到数组长度为 1 或 0,此时数组就已经排好序了。
一些注意事项:
Hoare 版本的快速排序可以避免对基准元素相同的元素的不必要交换操作,因此比 Lomuto 版本效率更高。
在实:现时需要注意边界条件的处理,尤其是数组下标越界问题。
在这里大家可能会有一个疑惑,两个指针i,j相遇的位置的值,如何保证要比key的值小呢?
答案:右边的值先走就能做到!!!
当右侧的值先走时,如果它遇到了一个比基准元素小的值,那么它就会停下来,等待左侧的值去找到一个比基准元素大的值。接着左侧的值找到了一个比基准元素大的值后,交换左右值的位置,此时右侧的值就会从原来的位置继续走下去。由于左侧的值都比基准元素小,所以右侧的值再次遇到比基准元素小的值时,仍然会停下来等待左侧的值交换位置。这样,右侧的值先走就可以保证最后相遇的位置的值一定比基准元素的值小。
在这里大家可能会有一个疑惑了,那我们能左边先走吗?
当然可以,同样的道理,只需要key值在右边就行了!
代码实现:
详见代码内注释
void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}int Partsort(int* a, int left, int right)
{int key = left;while (left < right){//找小,注意这里是右向左靠,在最后会的一步将是靠向左边while (left < right && a[right] >= a[key]){--right;}//找大while (left < right && a[left] <= a[key]){++left;}Swap(&a[left], &a[right]);//交换大于key和小于key位置的值}Swap(&a[key], &a[left]);//此时left==right,交换key和left的值return left;//用于递归下一步的key的左半边以及右半边}void Quicksort(int* a, int begin, int end)
{if (begin >= end)//当下标越界时,直接退出return;int key = Partsort(a, begin, end);// [begin, key-1] key [key+1, end] 大致形状Quicksort(a, begin, key - 1);//对左key半边进行排序Quicksort(a, key + 1, end);//对右半边进行排序
}
♒️快排的优化
先看个例子:(当我们的快排是排升序,然而我们要排的数据确是一个降序时)
对此,快速排序有相应的优化:采用“三点取中法”,即:在待排序序列中随机选取三个元素,取中间值作为key值。这样做是为了避免选取到最大或最小值作为 key值,从而导致快排算法性能下降的问题。
代码实现:
详见代码内注释
void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}int Getmid(int* a, int left, int right)//三点取中值
{int mid = (left + right) / 2;if (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] < a[right]){return right;}else{return left;}}else{if (a[mid] > a[right]){return mid;}else if (a[right] > a[left]){return left;}else{return right;}}
}int Partsort(int* a, int left, int right)
{int mid = Getmid(a, left, right);//三点取中值,优化Swap(&a[left], &a[mid]);//将中值放到最左边int key = left;while (left < right){//找小,注意这里是右向左靠,在最后会的一步将是靠向左边while (left < right && a[right] >= a[key]){--right;}//找大while (left < right && a[left] <= a[key]){++left;}Swap(&a[left], &a[right]);//交换大于key和小于key位置的值}Swap(&a[key], &a[left]);//此时left==right,交换key和left的值return left;//用于递归下一步的key的左半边以及右半边}void Quicksort(int* a, int begin, int end)
{if (begin >= end)//当下标越界时,直接退出return;int key = Partsort(a, begin, end);// [begin, key-1] key [key+1, end] 大致形状Quicksort(a, begin, key - 1);//对左key半边进行排序Quicksort(a, key + 1, end);//对右半边进行排序
}
2、 挖坑法
上来请先看一张图~
挖坑法的实现步骤如下:
设置左指针left和右指针right,以及基准元素key。
从右指针开始,向左遍历数组,直到找到第一个小于准元素key的元素,将其填入左指针所在位置的坑中,并将右指针指向左边一位。
从左指针开始,向右遍历数组,直到找到第一个大于准元素key的元素,将其填入右指针所在位置的坑中,并将左指针指向右边一位。
重复执行步骤2和步骤3,直到左指针等于右指针。
将基准元素填入最后的坑中。
对基准元素左边的子数组和右边的子数组,分别递归执行以上步骤,直到完成排序。
一些注意事项:
坑位可以是基准元素的位置,也可以是左指针或右指针的位置。
在填坑的过程中,需要先将当前指针指向的元素保存起来,然后将其填入对应的坑中。
在左指针等于右指针时,需要将基准元素填入最后的坑中。
代码实现:
详见代码内注释
void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}int Getmid(int* a, int left, int right)//三点取中值
{int mid = (left + right) / 2;if (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] < a[right]){return right;}else{return left;}}else{if (a[mid] > a[right]){return mid;}else if (a[right] > a[left]){return left;}else{return right;}}
}int Partsort2(int* a, int left, int right)
{int midi = Getmid(a, left, right);Swap(&a[left], &a[midi]);int key = a[left];// 保存key值以后,左边形成第一个坑int hole = left;while (left < right){// 右边先走,找小,填到左边的坑,右边形成新的坑位while (left < right && a[right] >= key){--right;}a[hole] = a[right];hole = right;// 左边再走,找大,填到右边的坑,左边形成新的坑位while (left < right && a[left] <= key){++left;}a[hole] = a[left];hole = left;}a[hole] = key;return hole;
}void Quicksort(int* a, int begin, int end)
{if (begin >= end)//当下标越界时,直接退出return;int key = Partsort2(a, begin, end);// [begin, key-1] key [key+1, end] 大致形状Quicksort(a, begin, key - 1);//对左key半边进行排序Quicksort(a, key + 1, end);//对右半边进行排序
}
3、前后指针法
上来请先看一张图~
前后指针法的实现步骤如下:
选取基准数。可以选择数组的第一个元素、最后一个元素、中间元素等。
设置两个指针,一个指向数组的第一个位置,另一个指向数组的最后一个位置。
从后往前遍历,找到第一个比基准数小的数,停止遍历。
从前往后遍历,找到第一个比基准数大的数,停止遍历。
交换两个数的位置。
重复步骤3到步骤5,直到前后指针相遇。
将基准数放到前后指针相遇的位置。
对基准数左右两边的子序列分别进行快速排序。
重复以上步骤,直到数组被完全排序。
一些注意事项:
在交换两个数的位置时,如果前后指针指向的数相同,那么就不能进行交换操作,因为这样会导致两个数的值发生变化。同时,在递归的过程中,需要对子序列进行边界检查,避免出现数组越界的情况。
代码实现:
详见代码内注释
void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}int Getmid(int* a, int left, int right)//三点取中值
{int mid = (left + right) / 2;if (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] < a[right]){return right;}else{return left;}}else{if (a[mid] > a[right]){return mid;}else if (a[right] > a[left]){return left;}else{return right;}}
}// 前后指针
int Partsort3(int* a, int left, int right)
{int midi = Getmid(a, left, right);Swap(&a[left], &a[midi]);int prev = left;//前指针,从最左边开始int cur = prev + 1;//后指针int keyi = left;while (cur <= right){if (a[cur] < a[keyi] && ++prev != cur)//当后指针cur找到比key小的值时,先让前指针++prev让prev到交换位置(因为原来比cur小1个位置)再判断前指针是否更cur相等,如果相等了就没有必要再交换了{Swap(&a[prev], &a[cur]);}++cur;//继续遍历找比key值小的值}Swap(&a[prev], &a[keyi]);//最后交换prev与key的值return prev;//此时prev在key最终位置,后续用于递归
}void Quicksort(int* a, int begin, int end)
{if (begin >= end)//当下标越界时,直接退出return;int key = Partsort3(a, begin, end);// [begin, key-1] key [key+1, end] 大致形状Quicksort(a, begin, key - 1);//对左key半边进行排序Quicksort(a, key + 1, end);//对右半边进行排序
}
感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o!
给个三连再走嘛~
相关文章:

【数据结构】—交换排序之快速排序究极详解,手把手带你从简单的冒泡排序升级到排序的难点{快速排序}(含C语言实现)
食用指南:本文在有C基础的情况下食用更佳 🔥这就不得不推荐此专栏了:C语言 ♈️今日夜电波:靴の花火—ヨルシカ 0:28━━━━━━️💟──────── 5:03 …...

【c#-Nuget 包“在此源中不可用”】 Nuget package “Not available in this source“
标题c#-Nuget 包“在此源中不可用”…但 VS 仍然知道它吗? (c# - Nuget package “Not available in this source”… but VS still knows about it?) 背景: 今日从公司svn 上拉取很久很久以前的代码,拉取下来200报错,进一步发…...

【数据结构】二叉树之堆的实现
🔥博客主页:小王又困了 📚系列专栏:数据结构 🌟人之为学,不日近则日退 ❤️感谢大家点赞👍收藏⭐评论✍️ 目录 一、二叉树的顺序结构 📒1.1顺序存储 📒1.2堆的性质…...

电工-三极管输入输出特性曲线讲解
三极管特性曲线是反映三极管各电极电压和电流之间相互关系的曲线,是用来描述晶体三极管工作特性曲线,常用的特性曲线有输入特性曲线和输出特性曲线。这里以下图所示的共发射极电路来分析三极管的特性曲线。 输入特性曲线 该曲线表示当e极与c极之间的电…...

深入解析容器与虚拟化:技术、对比与生态
深入解析容器与虚拟化:技术、对比与生态 文章目录 深入解析容器与虚拟化:技术、对比与生态容器和虚拟化的基本概念和原理容器的定义和特点虚拟化的定义和特点 容器使用场景容器和虚拟机的对比虚拟化技术的四个特点容器实现虚拟化的原理常见容器引擎和容器…...
制作游戏demo的心得
制作这个游戏demo出来的心得 https://www.bilibili.com/video/BV1cF411m7Dh/ 制作游戏demo的心得 制作游戏demo,主要是为了表现自己的技术,那就一门心思想着如何提高表现力就行了,在整体的画面渲染风格方面或许没有什么可选择的,…...

Web Tour Server窗口闪现
1.打开该文件所在位置 2.右击选择编辑,在最后一行加上pause,保存后重新打开Server窗口 3.重新打开后,若出现以下情况: 以管理员身份打开cmd命令行,输入命令netstat -aon|findstr “1080”,查看1080端口占用…...

Linux下的基本指令
目录 01. ls 指令 02. pwd命令 03. cd 指令 04. touch指令 05.mkdir指令(重要): 06.rmdir指令 && rm 指令(重要): 07.man指令(重要): 08mv指令ÿ…...
随机数生成器代码HTML5
代码如下 <!DOCTYPE html> <html> <head> <title>随机数生成器</title> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <style> body { text-align: center; bac…...

正确理解redux Toolkits中createSlice的action.payload
使用redux Toolkits中的createSlice编写extraReducers经常看到使用action.payload来更新state状态值: 那么action.payload指的到底是什么? 让我们看看action的定义部分: 注意: action.payload不是上面ajax请求的返回内容&#x…...

YOLOv8快速复现 官网版本 ultralytics
YOLOV8环境安装教程.:https://www.bilibili.com/video/BV1dG4y1c7dH/ YOLOV8保姆级教学视频:https://www.bilibili.com/video/BV1qd4y1L7aX/ b站视频:https://www.bilibili.com/video/BV12p4y1c7UY/ 1 平台搭建YOLOv8 平台:https://www.a…...

Haproxy搭建 Web 群集实现负载均衡
目录 1 Haproxy 1.1 HAProxy的主要特性 1.2 HAProxy负载均衡策略 1.3 LVS、Nginx、HAproxy的区别 2 Haproxy搭建 Web 群集 2.1 haproxy 服务器部署 2.1.1 关闭防火墙 2.1.2 内核配置(实验环境可有可无) 2.1.3 安装 Haproxy 2.1.4 Haproxy服务…...

Tessy 5.0.4
Tessy 5.0.4 Linux 2692407267qq.com,更多内容请见http://user.qzone.qq.com/2692407267/...
mybatis-plus根据指定条件批量更新
1.service实现类中 比如我这里只针对UserEntity,在UserServiceImpl下(该实现类是继承了mybatis-plus的ServiceImpl的)新增如下代码: public boolean updateBatchByQueryWrapper(Collection<UserEntity> entityList, Funct…...

虹科方案 | LIN/CAN总线汽车零部件测试方案
文章目录 摘要一、汽车零部件测试的重要性?二、虹科的测试仿真工具如何在汽车零部件测试展露头角?三、应用场景**应用场景1:方向盘开关的功能测试****应用场景2:各类型电机的控制测试****应用场景3:RGB氛围灯的功能测试…...
[solidity]合约调用合约
先写一个简单的合约将其部署,部署后的合约地址为:0xd9145CCE52D386f254917e481eB44e9943F39138 // SPDX-License-Identifier: MIT pragma solidity ^0.8.0;contract A{string myname;function setName(string memory _name) public{myname_name;}functi…...

Vulnhub系列靶机---JANGOW 1.0.1
文章目录 网卡配置信息收集主机发现端口扫描 漏洞利用反弹Shell提权 靶机文档:JANGOW 1.0.1 下载地址:Download (Mirror) 难易程度:. 网卡配置 水果味儿 信息收集 主机发现 端口扫描 访问80端口 点击site目录 点击页面上方的一个选项&…...

肖sir__项目环境之全流程__005
一、测试流程(h模型) 1、需求文档(产品) 需求文档(软件需求规格说明书srs) (1)如何分析需求 a、显示需求(主流程、功能,业务) b、隐性需求&#x…...
搜狗输入法下键翻页
搜狗输入法下键翻页 从官网下载 搜狗输入法智慧版关闭超级候选关闭候选...

C#多线程
一、多线程实现方式 1. 使⽤Thread类: System.Threading.Thread 类是C#中最基本的多线程编程⼯具。 2. 使⽤ThreadPool: 线程池是⼀个管理和重⽤线程的机制,它可以在应⽤程序中创建和使 ⽤多个线程,⽽⽆需显式地管理线程的…...

stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

mac:大模型系列测试
0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何,是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试,是可以跑通文章里面的代码。训练速度也是很快的。 注意…...

Selenium 查找页面元素的方式
Selenium 查找页面元素的方式 Selenium 提供了多种方法来查找网页中的元素,以下是主要的定位方式: 基本定位方式 通过ID定位 driver.find_element(By.ID, "element_id")通过Name定位 driver.find_element(By.NAME, "element_name"…...
OpenGL-什么是软OpenGL/软渲染/软光栅?
软OpenGL(Software OpenGL)或者软渲染指完全通过CPU模拟实现的OpenGL渲染方式(包括几何处理、光栅化、着色等),不依赖GPU硬件加速。这种模式通常性能较低,但兼容性极强,常用于不支持硬件加速…...
自定义线程池1.2
自定义线程池 1.2 1. 简介 上次我们实现了 1.1 版本,将线程池中的线程数量交给使用者决定,并且将线程的创建延迟到任务提交的时候,在本文中我们将对这个版本进行如下的优化: 在新建线程时交给线程一个任务。让线程在某种情况下…...