【六大排序详解】终篇 :冒泡排序 与 快速排序

终篇 :冒泡排序 与 快速排序

  • 1 冒泡排序
    • 1.1 冒泡排序原理
    • 1.2 排序步骤
    • 1.3 代码实现
  • 2 快速排序
    • 2.1 快速排序原理
      • 2.1.1 Hoare版本
        • 代码实现
      • 2.1.2 hole版本
        • 代码实现
      • 2.1.3 前后指针法
        • 代码实现
      • 2.1.4 注意
        • 取中位数
        • 局部优化
      • 2.1.5 非递归版本
        • 非递归原理
        • 代码实现
    • 2.2 特性总结
  • 谢谢阅读Thanks♪(・ω・)ノ
  • 下一篇文章见!!!

1 冒泡排序

1.1 冒泡排序原理

冒泡排序如同泡泡上升一样,逐个逐个向上冒,一个接一个的冒上去。两两比较,较大者(较小者)向后挪动。全部遍历一遍即可完成排序。
在这里插入图片描述

1.2 排序步骤

  1. 首先从头开始,两两相互比较。每次排好一个最大(最小)
  2. 然后在从头开始,两两比较 至已排序部分之前。
  3. 依次往复,全部遍历一遍。
  4. 完成排序。
    在这里插入图片描述

1.3 代码实现

以排升序为例


void BubbleSort(int* a, int n){for (int i = 0; i < n; i++) //从头开始遍历{//每次遍历 会排完一个 需排序部分减少 1 for (int j = 0; j < n - 1 - i;j++) {   //结束条件 a[n-2] > a[n-1]if (a[j] > a[j + 1]) //如果大,向上冒{      int tmp = a[j];a[j] = a[j + 1];a[j + 1] = tmp;}}}
}

排序结果,非常顺利。
在这里插入图片描述
冒泡排序的特性总结:

  1. 冒泡排序是一种非常容易理解的排序
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:稳定

2 快速排序

2.1 快速排序原理

快速排序是一种高效快速的算法,是Hoare于1962年提出的一种二叉树结构的交换排序方法,
其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
根据其思想 ,便有递归版本 和 非递归版本。
递归版本中有 Hoare版本, Hole版本,前后指针版本
非递归版本使用 栈 来实现

2.1.1 Hoare版本

Hoare版本是一种非常巧妙的版本,其思路大致为(以排升序为例)

  1. 确定一个key值,然后右找较大值,左找较小值
  2. 交换,直到左右相遇,
  3. 相遇时, 相遇位置的值一定小于key值(取决于先找大还是先找小,先找大,则为较小值,否则反之),交换key 与 相遇位置的值。
  4. 此时满足左边都比key小,右边都比key大
  5. 然后再分别进行左部分和右部分的排序。
  6. 全部递归完毕,排序完成。
    在这里插入图片描述
代码实现
//交换函数
void Swap(int* p1, int* p2) {int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
//key 取中位数
int getmidi(int *a,int begin,int end) {int midi = (begin + end) / 2;if (a[midi] > a[begin]) {if (a[midi] < a[end]) return midi;else if (a[begin] > a[end]) return begin;else return end;}else {//a[midi]<a[begin]if (a[begin] < a[end])	return begin;else if (a[midi] > a[end]) return midi;else return end;
}//Hoare版本快速排序
int PartSort1(int* a, int left, int right) {//取key 为首元素int keyi = left;//开始交换,右找大,左找小while (left < right) {//右找大while (left < right && a[right] >= a[keyi]) {right--;}//左找小while (left < right && a[left] <= a[keyi]) {left++;}//交换Swap(&a[left], &a[right]);}//将key与相遇位置值交换,//满足左边都比key小,右边都比key大Swap(&a[keyi], &a[left]);keyi = left;
}
//快速排序主体
void QuickSort(int* a, int begin, int end) {//递归实现if (begin >= end) return;// 定义左边,右边与key相应位置。int left = begin, right = end ;int keyi = begin;//该步骤优化十分重要。int midi = getmidi(a, begin, end);Swap(&a[left], &a[midi]);//排序int key = PartSort1(a, left, right);QuickSort(a, begin, key-1);QuickSort(a, key+1, end);
}

我们来看看运行效果。
在这里插入图片描述

2.1.2 hole版本

Hole版本即为挖坑法,是对Hoare版本的优化,避免了许多容易出现的错误。其基本思路为(排升序为例)

  1. 确定一个key值,在该处形成坑位
  2. 右找较大值,进入坑位,然后在该较大值处形成新的坑位
  3. 左找较小值,进入坑位,然后在该较小值处形成新的坑位。
  4. 。。。反复进行至相遇时,把key值放入该坑位。
  5. 此时满足左边都比key小,右边都比key大
  6. 然后再分别进行左部分和右部分的排序。
  7. 全部递归完毕,排序完成。
    在这里插入图片描述
代码实现

主体与上面的Hoare相同,这里提供挖坑法的函数部分。

int PartSort2(int* a, int left, int right) {int key = a[left]; //key取左值int holei = left;//开始排序while(left < right){//右找大while (a[right] >= key && right > left) {right--;}//进坑 挖坑a[holei] = a[right];holei = right;//左找小while (a[left] <= key && right > left) {left++;}//进坑 挖坑		a[holei] = a[left];holei = left;}//结束时,key进坑。完成排序a[holei] = key;return left;
}

2.1.3 前后指针法

前后指针算法是不同于上面两种的独特算法,较为简单。其基本思路为(以排升序为例):

  1. 首先取key值,并定义两个指针,分别指向当前位置与下一位置
  2. 如果cur 指向的值比key小,prev++。然后交换prev和cur指针指向的内容
  3. cur++;
  4. 直到cur到末位置。
  5. 交换key和prev指针指向的内容交换。
  6. 此时满足左边都比key小,右边都比key大
  7. 然后再分别进行左部分和右部分的排序。
  8. 全部递归完毕,排序完成。
    在这里插入图片描述
代码实现

主体与上面的Hoare相同,这里提供挖坑法的函数部分。

void Swap(int* p1, int* p2) {int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
int PartSort3(int* a, int begin, int end) {int key = a[begin];int prev = begin, cur = prev + 1;while (cur <= end) {if (a[cur] < key) {prev++;if (prev != cur)Swap(&a[cur], &a[prev]);}cur++;}Swap(&a[begin], &a[prev]);return prev;
}

2.1.4 注意

取中位数

接下来来看两组测试数据,一组为随机十万数据,一组为有序十万数据。
不取中位数版本 与 取中位数版本。
在这里插入图片描述
这是肉眼可见的性能提升,防止了再有序情况下的逐个遍历。因此取中位数是很重要的一步,当然一般情况下不会遇到最坏情况。

局部优化

根据二叉树的相关知识,最后一层包含50%数据,倒数第二层包含25%数据,倒数第三层包含12.5%数据。
第n层 递归 1 次 第 n-1 层 递归 2 次 第 n - 2 层 递归 4 次 … 第 1 层 递归 2^n 次
所以在进行绝大部分的排序后,如果继续进行递归会存在问题,此时递归次数非常多。所以我们进行局部优化,在数据小于10个(取决于具体数据)时改换为插入排序等稳定算法即可。

2.1.5 非递归版本

非递归算法通常要使用一些循环来达到全部遍历的目的。也使得 非递归版本 比 递归版本 更需要对“递归”的深入理解,这里快速排序的非递归版本使用栈来模拟递归过程

非递归原理

先看递归的实现过程,先对整体排,然后排左部分,排右部分。接着对左进行相同处理,对右进行相同处理。
这样的过程可以通过栈来实现(当然使用数组进行指定操作也可以)
在这里插入图片描述

栈里面依次存放了应该排序的部分,每次取出两个,来进行排序(注意取出顺序与存入顺序相反,若先入左 则先取的为右),排序完毕,存入左右部分的开始位置与结束位置,直到有序。
排序步骤

  1. 存入开始位置begin 结束位置end ,key值取左值。
  2. 依次出栈 记录右位置 right ,左位置 left(读取顺序很重要),排序 该部分
  3. 以key值分割左右两部分,压栈存入左部分的开始与结束位置,压栈存入右部分的开始与结束位置。(若left >= key不读取左部分 若 right<=key 不读取右部分)
  4. 依次出栈 记录右位置 right ,左位置 left(读取顺序很重要),排序 该部分
  5. 重复2 - 3步骤,直到栈为空。
  6. 完成排序
代码实现

需要使用栈的相应函数,栈的具体内容请看
栈相关知识


//非递归排序
void QuickSortNonR(int* a, int begin, int end) {//建立栈Stack s ;StackInit(&s);//初始化//压入开始与结束位置StackPush(&s, begin);StackPush(&s, end);//开始排序while (!StackEmpty(&s)) {//不为空就继续进行//出栈读入右位置int right = StackTop(&s);//读取后删除StackPop(&s);//出栈读入左位置int left = StackTop(&s);//读取后删除StackPop(&s);//对该部分进行排序 这里使用前后指针法(使用三种其一即可)int keyi = PartSort3(a, left, right);//读取左部分 若left>=key不进行读入if (left < keyi) {//入栈 StackPush(&s, left);StackPush(&s, keyi - 1);}//读取右部分 若right<=key不进行读入if (right > keyi) {//入栈StackPush(&s, keyi + 1);StackPush(&s, right);}		}
}

2.2 特性总结

快速排序的特性总结

  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

  2. 时间复杂度:O(N*logN)
    在这里插入图片描述

  3. 空间复杂度:O(logN)

  4. 稳定性:不稳定
    总的来说快速排序的内容十分丰富。我个人感觉使用前后指针来实现快速排序比较简单。同时非递归版本可以让我们更深刻的认识递归过程。而且不同版本的性能大差不差,基本相同。

谢谢阅读Thanks♪(・ω・)ノ

下一篇文章见!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/598874.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

室内效果图没有质感?外国大神这6个实用技巧,带你轻松掌握!

为了创作出高级有质感的效果图&#xff0c;我们需要注意构图、颜色、布光等多种因素&#xff0c;以打造出逼真的渲染效果。不过不要担心&#xff01;今天小编带来了国外知名设计师Arch Viz Artist在油管上分享的6个实用小技巧。看完带你轻松提升室内效果图的表现力&#xff01;…

WPS/PPT插件-大珩助手免费功能更新-特殊字符

扩展特殊格式下特殊字符&#xff0c;增加200多个常用特殊字符&#xff0c;可直接点击插入。 PPT大珩助手 1.7.6 1、提供素材库功能&#xff0c;可实现一键保存素材&#xff0c;支持对选中的形状&#xff0c;支持一键替换素材&#xff0c;保留原素材的尺寸和位置&#xff0c;…

RTT打印时间戳

官方的RTT VIEWER没有打印接收时间戳的功能&#xff0c;经过查找后发现可以有以下三种打印时间戳的方法。 第三方的RTT上位机ExtraPutty自己打印 第三方的RTT上位机 码云上有一个RTT_T2的仓库&#xff0c;基于python qt包写的画面&#xff0c;通过pylink来jlink通信。 优点…

Nginx 负载均衡集群 节点健康检查

前言 正常情况下&#xff0c;nginx 做反向代理负载均衡的话&#xff0c;如果后端节点服务器宕掉的话&#xff0c;nginx 默认是不能把这台服务器踢出 upstream 负载集群的&#xff0c;所以还会有请求转发到后端的这台服务器上面&#xff0c;这样势必造成网站访问故障 注&#x…

Linux GDB 调试

文章目录 一、Qemu二、Gdbvscode 调试 三、RootFs 一、Qemu qemu 虚拟机 Linux内核学习 Linux 内核调试 一&#xff1a;概述 Linux 内核调试 二&#xff1a;ubuntu20.04安装qemu Linux 内核调试 三&#xff1a;《QEMU ARM guest support》翻译 Linux 内核调试 四&#xff1a;…

基于SSM框架和Layui框架的管理系统

计算机毕业设计&#xff1a;打造安全、高效的信息管理系统在这个数字化时代&#xff0c;信息安全和高效管理是至关重要的。为了帮助学校或机构更好地管理和保护信息&#xff0c;我们为您设计了一套功能强大的信息管理系统。该系统利用先进的技术&#xff0c;结合MD5加密&#x…

使用Go语言的HTTP客户端进行并发请求

Go语言是一种高性能、简洁的编程语言&#xff0c;它非常适合用于构建并发密集型的网络应用。在Go中&#xff0c;标准库提供了强大的HTTP客户端和服务器功能&#xff0c;使得并发HTTP请求变得简单而高效。 首先&#xff0c;让我们了解为什么需要并发HTTP请求。在许多应用场景中…

【Nacos专题】Nacos如何建立与应用服务之间的通信渠道?

作为Spring Cloud Alibaba微服务架构实战派上下册和RocketMQ消息中间件实战派上下册的作者胡弦。 Nacos是一款面向云原生服务的注册中心和配置中心技术解决方案&#xff0c;既然要与服务打交道&#xff0c;那么通信渠道是必不可少的组件&#xff0c;那么Nacos是如何建立与应用…

【后端已完成,前端更新ing】uniapp+springboot实现个人备忘录系统【前后端分离】

目录 &#xff08;1&#xff09;项目可行性分析 &#xff08;一&#xff09;技术可行性&#xff1a; &#xff08;二&#xff09;经济可行性&#xff1a; &#xff08;三&#xff09;社会可行性&#xff1a; &#xff08;2&#xff09;需求描述 功能模块图 用例图&#…

Xcode15在iOS12系统上崩溃的原因

1.1.崩溃在_dyld_start&#xff0c;如图&#xff1a; 崩溃截图 解决办法&#xff1a;在other link flags添加-ld64 注意&#xff1a;该办法只能解决运行真机&#xff0c;archive出来的包依然报错闪退...... 1.2 SwiftUI导致iOS12及以下系统闪退问题 SwiftUI是iOS13开始使用&…

(NeRF学习)NeRF复现 win11

目录 一、获取源码二、环境三、准备数据集1.下载数据集方法一&#xff1a;官方命令方法二&#xff1a;官网下载数据集 2.修改配置 四、开始训练1.更改迭代次数2.开始训练方法一&#xff1a;方法二&#xff1a; 3.使用预训练模型 五、NeRF源码学习 一、获取源码 git clone http…

C#之反编译之路(一)

本文将介绍微软反编译神器dnSpy的使用方法 c#反编译之路(一) dnSpy.exe区分64位和32位,所以32位的程序,就用32位的反编译工具打开,64位的程序,就用64位的反编译工具打开(个人觉得32位的程序偏多,如果不知道是32位还是64位,就先用32位的打开试试) 目前只接触到wpf和winform的桌…

什么是负载均衡?什么情况下又会用到负载均衡

什么是负载均衡 在大型的网络应用中&#xff0c;使用多台服务器提供同一个服务是常有的事。平均分配每台服务器上的压力、将压力分散的方法就叫做负载均衡。 [利用 DNS来实现服务器流量的负载均衡&#xff0c;原理是“给网站访问者随机分配不同ip”] 什么情况下会用到负载均…

芯课堂 | LVGL基础知识(三)

概述 LVGL进度条对象上有一个背景和一个指示器。指示器的宽度根据进度条的当前值进行设置。 如果对象的宽度小于其高度&#xff0c;则可以创建垂直进度条。 不仅可以设置进度条的结束值&#xff0c;还可以设置进度条的起始值&#xff0c;从而改变指示器的起始位置。 LVGL进度…

使用EasyExcel导出百万条数据

使用EasyExcel导出百万条数据 应用是基于100W条数据进行的测试 首先&#xff1a;导入相关需要的依赖&#xff1a; <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.16</version></depend…

springboot系列——IDEA创建项目并运行

springboot Spring Boot是为了简化Spring应用程序的开发和部署而产生的。 Spring Boot提供了一种基于约定优于配置的开发模式。它自动配置了Spring应用程序所需的各种组件和依赖&#xff0c;并提供了简单易用的命令行工具来构建和运行应用程序。 Spring Boot还提供了一套开箱…

Existing installation is up to date

这个报错是之前安装的docker没有删除干净 解决方法&#xff1a; 打开注册表编辑器 然后再搜索栏&#xff1a;HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Docker Desktop 回车 找到Docker Desktop文件夹后&#xff0c;右键删除 重新安装Docker…

Security的入门和流程分析

Security的入门和流程分析 问题&#xff1a;访问一个controller方法之前进行一个权限验证&#xff1f; 在controller里面的每一个handler无论什么访问都要进行一个校验&#xff0c;但是对于login logout 验证码这种Handler处理器是放行的 1.使用过滤器拦截器 注意两者区别 过滤…

DataGrip 数据库备份

一、备份 1、找到需要被备份的数据库demo&#xff0c;右键>Import/Export>Export with mysqldump 2、配置路径 点击run&#xff0c;等待完成 导出成功 二、还原 选择 需要导入的数据库>右键>Import/Export>Restore with ‘mysql’ 点击run&#xff0c;刷…

基于seatunnel实现mysql同步clickhouse验证

场景&#xff1a; 需求想要实现mysql同步到clickhouse&#xff0c;seatunnel部署见前面文档linux环境seatunnel安装运行-CSDN博客。 官方说明文档 Clickhouse | Apache SeaTunnel mysql同步配置 server-id1 log_bin/var/lib/mysql/bin.log binlog_formatROW #binlog-do-db 具…