排序 “叁” 之交换排序


目录

1. 基本思想

2.冒泡排序

2.1 基本思想

2.2 代码示例

2.3 冒泡排序的特性总结

3.快速排序

3.1 基本思想

🌵hoare版本

🌵挖坑法

​编辑

🌵前后指针版本

​编辑

3.2 快速排序优化

🌻三数取中法选key

3.4 快速排序的特性总结


1. 基本思想

        所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

2.冒泡排序

2.1 基本思想

冒泡排序是一种简单直观的排序算法,其基本思想是通过不断比较相邻的元素并交换位置,将最大(或最小)的元素逐步“冒泡”到最后(或最前)。

冒泡排序的具体步骤:

  1. 比较相邻元素:从数组的第一个元素开始,依次比较相邻的两个元素,如果顺序不对则交换它们的位置,确保较大的元素向后移动。
  2. 一轮冒泡:经过一轮比较和交换操作,最大的元素会沉到数组的最后位置。重复这个过程,直到没有需要交换的元素为止。
  3. 多轮冒泡:重复进行上述步骤,每次冒泡操作都会将当前未排序部分的最大元素移动到正确的位置。经过多轮冒泡,整个数组就会逐步有序。
  4. 优化:可以在每一轮冒泡中记录是否有元素交换的标志,如果某一轮没有元素交换,说明数组已经有序,可以提前结束排序。

下面是一个简单的示例,演示冒泡排序的步骤:

假设要排序的数组为:[5, 3, 8, 2, 1]

  • 第一轮冒泡:比较并交换,数组变为 [3, 5, 2, 1, 8]
  • 第二轮冒泡:比较并交换,数组变为 [3, 2, 1, 5, 8]
  • 第三轮冒泡:比较并交换,数组变为 [2, 1, 3, 5, 8]
  • 第四轮冒泡:比较并交换,数组变为 [1, 2, 3, 5, 8]

经过四轮冒泡排序,数组变为有序状态:[1, 2, 3, 5, 8]。

2.2 代码示例

//交换
void Swap(int* p, int* q)
{int tmp = *p;*p = *q;*q = tmp;
}//冒泡排序
void BubbleSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){//标志位,用于记录本轮是否发生了元素交换bool flag = false;for (int j = 0; j < n - i - 1; j++){if (a[j + 1] < a[j]){//交换相邻元素Swap(&a[j + 1], &a[j]);flag = true;}}//如果本轮没有发生元素交换,说明数组已经有序,提前结束排序if (!flag)break;}
}//打印
void PrintSort(int* a, int n)
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}//测试
int main()
{int a[] = { 1, 5, 7, 9, 0, 2, 4, 8, 3, 8, 6 };BubbleSort(a, sizeof(a) / sizeof(int));PrintSort(a, sizeof(a) / sizeof(int));return 0;
}

2.3 冒泡排序的特性总结

  1. 时间复杂度:冒泡排序的平均时间复杂度为O(n^2),最坏情况下为O(n^2),最好情况下为O(n)。因此,冒泡排序对于大规模数据集并不是一个高效的选择。
  2. 空间复杂度:冒泡排序的空间复杂度为O(1),因为它只需要一个额外的临时变量来进行元素交换。
  3. 稳定性:冒泡排序是一种稳定的排序算法,相同元素的相对位置在排序前后不会改变。
  4. 适用场景:由于冒泡排序的效率较低,通常不推荐在实际应用中使用。但对于小规模数据集或者教学目的,冒泡排序是一个很好的入门算法。
  5. 优缺点:冒泡排序的优点是实现简单,代码易于理解;缺点是效率低下,不适用于大规模数据集。

3.快速排序

3.1 基本思想

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,它的核心思想是通过分治法来实现排序。

具体步骤如下:

  1. 选择一个基准值(通常选择第一个元素)。
  2. 将小于基准值的元素移到基准值的左边,将大于基准值的元素移到基准值的右边。
  3. 对基准值左右两侧的子数组分别递归地进行快速排序。

将区间按照基准值划分为左右两半部分的常见方式有:

🌵hoare版本

思想步骤:

  1. 选择一个基准元素(通常选择数组的中间元素)。
  2. 使用两个指针,一个指向数组的起始位置,另一个指向数组的末尾位置。
  3. 右指针向左移动,直到找到一个小于基准元素的元素。
  4. 左指针向右移动,直到找到一个大于基准元素的元素。
  5. 左右指针分别找到元素后,交换它们各自位置的数值。
  6. 重复步骤3到步骤5,直到左指针和右指针相遇,将该位置的值与基准元素位置的值交换。
  7. 此时基准元素左边的元素都小于等于它,右边的元素都大于等于它。
  8. 递归地对基准元素左右两边的子数组进行快速排序。

//交换
void Swap(int* p, int* q)
{int tmp = *p;*p = *q;*q = tmp;
}//快速排序
void QuickSort(int* a, int begin, int end)
{//如果子数组长度为1或0,则直接返回if (begin >= end)return;//初始化左右指针和基准值int left = begin, right = end;int keyi = begin;while (left < right){//从右向左找第一个小于基准值的元素while (left < right && a[right] >= a[keyi]){--right;}//从左向右找第一个大于基准值的元素while (left < right && a[left] <= a[keyi]){++left;}//将找到的元素位置进行交换Swap(&a[right], &a[left]);}//将基准元素放到正确的位置上Swap(&a[left], &a[keyi]);keyi = left;//递归的对基准元素左右两侧的子数组进行快速排序QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}//打印
void PrintSort(int* a, int n)
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}//测试
int main()
{int a[] = { 1, 5, 7, 9, 0, 2, 4, 8, 3, 8, 6 };QuickSort(a, 0, sizeof(a) / sizeof(int) - 1);PrintSort(a, sizeof(a) / sizeof(int));return 0;
}

🌵挖坑法

思想步骤:

  1. 先将第一个数据存放在临时变量key中,形成一个坑位。
  2. right开始向前移动,找比key位置小的值,找到后将right位置的值放进坑里,此时right位置作为新的坑。
  3. left开始向后移动,找比key位置大的值,找到后将left位置的值放进坑里,此时left位置作为新的坑。
  4. right接着向前找,left接着向后找,直到leftright相遇。
  5. key放入相遇时的坑里,排序完毕。

// 挖坑法
int PartSort(int* a, int begin, int end)
{//选择第一个元素作为基准元素int key = a[begin];int hole = begin;while (begin < end){//从右往左找到第一个小于基准元素的元素while (begin < end && a[end] >= key){--end;}//将找到的元素填入左边的坑a[hole] = a[end];//更新坑的位置hole = end;//从左往右找到第一个大于基准元素的元素while (begin < end && a[begin] <= key){++begin;}//将找到的元素填入右边的坑a[hole] = a[begin];//更新坑的位置hole = begin;}//将基准元素放入最终的坑a[hole] = key;return hole;
}void QuickSort(int* a, int begin, int end)
{//如果子数组长度为1或0,则直接返回if (begin >= end)return;int keyi = PartSort(a, begin, end);QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}//打印
void PrintSort(int* a, int n)
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}//测试
int main()
{int a[] = { 1, 5, 7, 9, 0, 1, 2, 4, 8, 1, 3, 8, 6 };QuickSort(a, 0, sizeof(a) / sizeof(int) - 1);PrintSort(a, sizeof(a) / sizeof(int));return 0;
}

 

🌵前后指针版本

思想步骤:

  1. 选择一个基准元素作为key(通常选择数组的第一个元素)。
  2. 使用两个指针,前指针prev指向序列开头,后指针cur指向prev指针的后一个位置。
  3. 判断cur指针指向的数据是否小于key,若小于,则prev指针后移一位(注意:prev只有在cur找到比key小的数时才加1),并将cur指向的内容与prev指向的内容交换,然后cur指针++。若大于,则cur指针++。
  4. 依次类推直到cur遍历完整个数组,最后将prev位置的值与key位置的值进行交换,则完成单趟排序。

//交换
void Swap(int* p, int* q)
{int tmp = *p;*p = *q;*q = tmp;
}//前后指针法
int PartSort(int* a, int begin, int end)
{int key = begin;int prev = begin;int cur = prev + 1;while (cur <= end){//如果cur位置的值小于key位置的值,//并且prev位置++后的值如果和cur位置的值不相等,//就交换prev位置和cur位置的值if (a[cur] < a[key] && ++prev != cur)Swap(&a[prev], &a[cur]);++cur;}Swap(&a[prev], &a[key]);key = prev;return key;
}void QuickSort(int* a, int begin, int end)
{//如果子数组长度为1或0,则直接返回if (begin >= end)return;int keyi = PartSort(a, begin, end);QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}


3.2 快速排序优化

🌻三数取中法选key

三数取中法是在快速排序算法中用于选择基准元素的一种策略。它的思想是从待排序数组中随机选择三个元素,然后取它们的中间值作为基准元素,这样可以尽量避免选择到极端的值作为基准元素,从而提高快速排序的效率。

使用三数取中法选择基准元素的具体步骤:

  1. 从待排序数组中随机选择三个元素,通常是选择数组的第一个元素、中间元素和最后一个元素。
  2. 比较这三个元素,找到它们的中间值作为基准元素。可以使用简单的比较排序或者其他方法来找到中间值。
  3. 将选定的基准元素放置在数组的最左边(或者其他位置),并记录其值。
  4. 接下来的快速排序过程中,使用这个选定的基准元素进行分区操作,将小于基准元素的元素放在左边,大于基准元素的元素放在右边。

使用三数取中法选择基准元素可以有效地避免选择到极端值作为基准元素,从而提高快速排序的效率,减少最坏情况下的时间复杂度。这种方法在实际应用中被广泛采用,用以提高快速排序的性能。

🍂代码示例:

//交换
void Swap(int* p, int* q)
{int tmp = *p;*p = *q;*q = tmp;
}//三数取中
int GetMidi(int* a, int begin, int end)
{//选择三个元素的中间值作为基准元素int midi = (begin + end) / 2;//确保 a[begin] <= a[midi] <= a[end]if (a[begin] < a[midi]){if (a[midi] < a[end])return midi;else if (a[begin] > a[end])return begin;elsereturn end;}else //a[begin] > a[midi]{if (a[midi] > a[end])return midi;else if (a[begin] < a[end])return begin;elsereturn end;}
}//快速排序
void QuickSort(int* a, int begin, int end)
{//如果子数组长度为1或0,则直接返回if (begin >= end)return;int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);//初始化左右指针和基准值int left = begin, right = end;int keyi = begin;while (left < right){//从右向左找第一个小于基准值的元素while (left < right && a[right] >= a[keyi]){--right;}//从左向右找第一个大于基准值的元素while (left < right && a[left] <= a[keyi]){++left;}//将找到的元素位置进行交换Swap(&a[right], &a[left]);}//将基准元素放到正确的位置上Swap(&a[left], &a[keyi]);keyi = left;//递归的对基准元素左右两侧的子数组进行快速排序QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}//测试
int main()
{int a[] = { 1, 5, 7, 9, 0, 2, 4, 8, 3, 8, 6 };QuickSort(a, 0, sizeof(a) / sizeof(int) - 1);PrintSort(a, sizeof(a) / sizeof(int));return 0;
}

3.4 快速排序的特性总结

  1. 时间复杂度:平均时间复杂度为O(N*logN),最坏情况下为O(N^2),最好情况下为O(N*logN)
  2. 空间复杂度:快速排序是一种原地排序算法,不需要额外的存储空间,空间复杂度为O(1)
  3. 稳定性:快速排序是一种不稳定的排序算法,即相同元素的相对位置可能会发生变化。
  4. 分治思想:快速排序使用分治思想,将数组分为两部分,分别对左右子数组进行排序。

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

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

相关文章

如何在群晖NAS部署office系统办公服务并实现无公网IP远程编辑文件

文章目录 本教程解决的问题是&#xff1a;1. 本地环境配置2. 制作本地分享链接3. 制作公网访问链接4. 公网ip地址访问您的分享相册5. 制作固定公网访问链接 本教程解决的问题是&#xff1a; 1.Word&#xff0c;PPT&#xff0c;Excel等重要文件存在本地环境&#xff0c;如何在编…

Umi.js:登录之后需要手动刷新权限菜单才能渲染

在使用Umi.js开发后台管理页面时&#xff0c;用户登录之后&#xff0c;总是需要手动刷新一次页面&#xff0c;才能够拿到全局状态/权限信息。 问题描述 结合使用umi/plugin-layout和umi/plugin-access&#xff0c;登录进入页面&#xff0c;配置的权限菜单未渲染&#xff0c;需…

javaWeb项目-大药房管理系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、Java语言简介 Ja…

【已解决】win10系统 Docker 提示Docker Engine stopped解决全过程记录

【已解决】win10系统 Docker 提示Docker Engine stopped解决全过程记录 一、检查服务是否开启 找到 【Docker Desktop Service】&#xff0c;然后&#xff0c;启动他&#xff1b; 你也可以直接设置为“自动” 找到服务&#xff0c;右键》属性》启动类型&#xff1a;自动》点击…

三. TensorRT基础入门-TensorRT简介

目录 前言0. 简述1. 什么是TensorRT2. TensorRT的工作流介绍3. TensorRT的一些限制总结参考 前言 自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考 本次课程我们来学习课程第三章—TensorRT 基础入门&#xf…

三、Flask模型基础

ORM 创建模型 # exts.py:插件管理 # 扩展的第三方插件 # 1.导入第三方插件 from flask_sqlalchemy import SQLAlchemy # ORM插件 from flask_migrate import Migrate # 2. 初始化 db = SQLAlchemy() # ORM migrate = Migrate() # 数据迁移 # 3. 和app对象绑定 def init_ex…

kafka实验部署

一、前期准备 二、kafka实验 在zookeeper后继续进行操作 2.1 为ndoe1、node2、node3作出部署 2.1.1 解压kafka压缩包&#xff08;node1举例&#xff09; 2.1.2 操作 将解压后的kafka移动到kafka&#xff0c;进入到kafka下的config中&#xff0c;复制文件 2.1.2.1 编辑server.pr…

【笔试训练】day7

1.在字符串中找出连续最长的数字串 思路&#xff1a; 简单双指针&#xff0c;随便怎么暴力 代码&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1 #include <iostream> #include<string> using namespace std;int main() {string str;cin >> str;int ans …

聚道云软件连接器助力医疗器械有限公司打通金蝶云星辰与飞书

摘要 聚道云软件连接器成功将金蝶云星辰与飞书实现无缝对接&#xff0c;为某医疗器械有限公司解决采购订单、付款单同步、审批结果回传、报错推送等难题&#xff0c;实现数字化转型升级。 客户介绍 某医疗器械有限公司是一家集研发、生产、销售为一体的综合性医疗器械企业。…

【linux运维】系统常见管理命令

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了学习基本的shell编程和linux命令&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于b站大学——linux运维课程进行的&#xff0c;…

安装多个MySQL版本时如何连接到不同的数据库

当安装多个版本的数据库时&#xff0c;不同版本的端口名不一样&#xff0c;可以使用以下命令进行连接 mysql -uroot -p数据库密码 -h主机名 -P端口号 数据库主机名默认是localhost&#xff0c;端口号默认是3306&#xff0c;当安装多个版本数据库时&#xff0c;需要记住数据库的…

【多线程】CAS的应用 | CAS的概念 | 实现原子类 | 实现自旋锁

文章目录 一、CAS1.什么是CAS2.实现原子类3.实现自旋锁 一、CAS 1.什么是CAS Compare and swap 比较并交换。 比较交换的是 内存 和 寄存器 比如此时有一个内存 : M。 还有两个寄存器A,B ​ CAS ( M , A , B ) :如果M和A的值相同的话&#xff0c;就把M和B的值进行交换(交换的…

排序之插入排序:从斗地主到插入排序

目录 1.斗地主如何摸牌 2.从摸牌想到插入排序 3.完成插入排序 4.结束语 1.斗地主如何摸牌 不知道各位是否玩过几乎人人都玩过的斗地主游戏呢&#xff1f;相必各位或多或少都玩过一点&#xff0c;再没玩过也看别人打过。今天博主就将从这个游戏为大家讲解我们的插入排序。 在…

React vs Vue.js:两种流行框架背后的设计哲学差异

Vue.js 和 React 虽然都是流行的前端框架,但在设计哲学上有一些显著差异。这些差异源自它们不同的设计目标和出发点。 vue.js Vue.js 的核心设计原则是"渐进式"。它旨在尽可能降低学习成本和入门门槛,并与其他库和项目保持良好的整合性。Vue 的设计注重简单性和直观…

C语言堆区内存管理

一、C语言编译的内存分配 二、堆区空间的分配 1、malloc函数 功能&#xff1a;从堆区分配内存 #include <stdlib.h> void *malloc(unsigned int size)//size 分配内存的字节数2、free函数 功能&#xff1a;释放内存 #include <stdlib.h> void free(void *ptr)…

数据恢复宝典:应对磁盘损坏无法读取的终极攻略

当电脑屏幕上突然弹出“磁盘损坏无法读取”的提示时&#xff0c;许多人的心情都会瞬间跌入谷底。那些存储在磁盘中的重要文件、珍贵的回忆&#xff0c;似乎在一瞬间都化为乌有。面对这样的困境&#xff0c;我们该如何应对&#xff1f;本文将深入探讨磁盘损坏无法读取的原因&…

常见的经典目标检测算法

目标检测是计算机视觉领域的一个核心任务&#xff0c;它涉及到识别图像中的物体并确定它们的位置。以下是一些常见的经典目标检测算法&#xff1a; R-CNN系列 R-CNN&#xff08;Region-based Convolutional Neural Network&#xff09;是一种用于目标检测的算法&#xff0c;它…

深度学习500问——Chapter08:目标检测(2)

文章目录 8.2.4 R-FCN 8.2.5 FPN 8.2.6 Mask R-CNN 8.2.4 R-FCN R-FCN 有哪些创新点 R-FCN仍然属于two-stage目标检测算法&#xff1a;RPN R-FCN Fully convolutional位置敏感得分图&#xff08;position-sentive score maps&#xff09; our region-based detector is ful…

OpenVINO安装教程 npm版

从 npm Registry安装 OpenVINO™ 工具套件的英特尔发行版 请注意&#xff1a; 仅提供 JavaScript API 专用于所有主要操作系统的用户&#xff1a;Windows、Linux 和 macOS &#xff08;所有 x86_64 / ARM64 架构&#xff09; macOS 仅支持 CPU 推理 系统要求软件要求 Window…

Python 数据结构和算法实用指南(二)

原文&#xff1a;zh.annas-archive.org/md5/66ae3d5970b9b38c5ad770b42fec806d 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第四章&#xff1a;列表和指针结构 我们已经在 Python 中讨论了列表&#xff0c;它们方便而强大。通常情况下&#xff0c;我们使用 Python…