数据结构——冒泡、选择、插入和希尔排序

目录

引言

冒泡排序

1.算法思想

2.算法步骤

3.代码实现

4.复杂度分析

选择排序

1.算法思想

2.算法步骤

3.代码实现

(1)优化前

(2)优化后

4.复杂度分析

插入排序

1.算法思想

2.算法步骤

3.代码实现

4.复杂度分析

希尔排序

1.算法思想

2.算法步骤

3.代码实现

4.复杂度分析

结束语


引言

在数据处理、算法优化等领域中,排序是基础且关键的一环。本文将要探讨的四种排序算法:冒泡排序、选择排序、插入排序和希尔排序

求点赞收藏关注!!!

冒泡排序

1.算法思想

冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复进行的,直到没有再需要交换的元素为止,这意味着数列已经排序完成。

这个算法的名字由来是因为越小(或越大)的元素会经由交换慢慢“浮”到数列的顶端(或底部)。

2.算法步骤

1.比较相邻的元素。如果第一个比第二个大(或小,根据排序顺序要求),就交换它们两个。

2.对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数(或最小的数)。

3.针对所有的元素重复以上的步骤,除了已完成排序元素。

4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较

注意:如果一轮之后,元素并没有发生任何交换,此时说明此时排序已经完成,那么我们可以提前结束循环。

我们来看个动图就能很直观的理解什么是冒泡排序:

3.代码实现

void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void bubble_sort(int arr[], int sz)
{// 外层循环,控制排序的轮数for (int i = 0; i < sz - 1; i++){// 定义一个标志位,用于判断是否在这一轮中有元素交换int flag = 0;// 内层循环,进行实际的元素比较和交换for (int j = 0; j < sz - i - 1; j++){// 如果当前元素大于后一个元素,则交换它们if (arr[j] > arr[j + 1]){// 设置标志位为1,表示发生了交换flag = 1;Swap(&arr[j], &arr[j + 1]);}}// 如果这一轮没有发生任何交换// 说明数组已经有序,可以提前结束排序if (flag == 0){break;}}
}

4.复杂度分析

时间复杂度:最坏情况是数组完全逆序,此时每一轮都需要进行 n - 1 次比较,并且每一轮都会进行至少一次交换.因此,总的比较次数和交换次数都接近 n (n - 1) / 2,其中 n 是数组的长度。所以,最坏情况下的时间复杂度是 O(n ^ 2) 。

空间复杂度:由于没有开辟额外的空间大小,因此空间复杂度为O(1)。

选择排序

1.算法思想

选择排序(Selection Sort)是一种简单直观的排序算法。通过不断选择剩余元素之中的最小(或最大)元素,然后与起始位置的元素交换(起始位置在每一次选择后都向后移动一位),直到整个序列排序完成。

2.算法步骤

1.在未排序序列中找到最小(大)元素。遍历未排序的数组,找到最小(或最大)的元素。

2.存放到排序序列的起始位置。将找到的最小(或最大)元素与未排序序列的第一个元素交换位置(如果第一个元素就是最小(大)元素,则它自己和自己交换)。

3.从剩余未排序元素中继续寻找:在剩下的未排序元素中继续执行步骤1和步骤2,直到所有元素都被排序。

 看动图直观的感受一下:

3.代码实现

(1)优化前
void SelectSort(int* arr, int len)
{for (int i = 0; i < len - 1; i++){// 假设当前位置i的元素是最小的,记录其索引为miniint mini = i;for (int j = i + 1; j < len; j++){// 如果发现更小的元素,则更新mini为当前更小元素的索引if (arr[j] < arr[mini]){mini = j;}}swap(&arr[mini], &arr[i]);}
}
(2)优化后

我们可以对上面的代码进行点优化,我们可以同时选择最大与最小的元素,同时往起始与结尾位置交换。

注意:同时交换可能会改变原先最大或者最小元素的位置。因此我们需要进行判断。

代码如下:

//交换两个数据
void Swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}void SelectSort(int* arr, int n)
{int begin = 0;		// 未排序部分的起始索引int end = n - 1;	// 未排序部分的结束索引while (begin < end){int maxi = begin;int mini = begin;// 遍历未排序部分,找到最小值和最大值for (int i = begin + 1; i <= end; i++){if (arr[i] < arr[mini]){mini = i;	// 更新最小值的位置}if (arr[i] > arr[maxi]){maxi = i;	// 更新最大值的位置}}// 将当前范围的最小值交换到未排序部分的开始位置Swap(&arr[begin], &arr[mini]);// 如果begin与maxi重合,则更新maxiif (maxi == begin){maxi = mini;}// 将当前范围的最大值交换到未排序部分的结束位置Swap(&arr[end], &arr[maxi]);// 缩小未排序部分的范围++begin;--end;}
}

4.复杂度分析

时间复杂度:由于每次外层循环中的内层循环需要遍历几乎所有未排序的元素,因此时间复杂度为O(n^2)。

空间复杂度:由于没有开辟额外的空间大小,因此空间复杂度为O(1)。

插入排序

1.算法思想

插入排序(Insertion Sort)是一种简单直观的排序算法。它模拟了我们日常生活中整理扑克牌或排序书籍的过程。其基本思想是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、元素个数加一的有序数据,直到全部待排序的数据元素插完,排序完成。

2.算法步骤

1.将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。

2.遍历未排序部分,将扫描到的每个元素插入有序序列的适当位置。

3.依次重复1,2步骤,直至插入完成。

 动图演示如下:

3.代码实现

void InsertSort(int* arr, int n)
{for (int i = 0; i < n - 1; i++){int end = i;// tmp 存储当前需要插入的元素的值int tmp = arr[end + 1];// 内层循环,将比 tmp 大的元素向后移动一位// 为 tmp 找到正确的插入位置while (end >= 0){if (arr[end] > tmp){arr[end + 1] = arr[end];end--;}else{break;}}// 将 tmp 插入到找到的正确位置arr[end + 1] = tmp;}
}

4.复杂度分析

时间复杂度:当处于最坏情况时,由于每次插入都会移动数据,因此时间复杂度为O(n^2)。

空间复杂度:由于没有开辟额外的空间大小,因此空间复杂度为O(1)。

希尔排序

1.算法思想

希尔排序(Shell Sort)是插入排序的一种更高效的改进版本,也称为缩小增量排序。基本思想是将待排序的数组元素按照某种增量(gap)进行分组,对每组使用插入排序算法进行排序。随着增量的逐渐减少,每组包含的元素越来越多,当增量减至1时,整个数组被视为一组进行最后的插入排序,从而完成排序过程。

2.算法步骤

1.选择一个增量 gap ,对数据进行分组,每间隔gap个元素分为一组,一共gap组。

2.以gap为基准单位,对其进行插入排序。

3.逐渐缩小gap的范围,直至gap为1,相当于进行一次正常的插入排序。

 动图演示所下所示:

3.代码实现

void ShellSort(int* arr, int n)
{// 初始化增量gap为数组长度n,用于分组int gap = n;while (gap > 1){// 逐渐减少增量gap = gap / 3 + 1;// 对每个分组进行插入排序for (int i = 0; i < n - gap; i++){int end = i;int tmp = arr[end + gap];while (end >= 0){// 如果当前位置的元素大于tmp// 则将当前位置的元素向后移动gap位if (arr[end] > tmp){arr[end + gap] = arr[end];end -= gap;}else{break;}}// 将tmp插入到找到的正确位置arr[end + gap] = tmp;}}
}

4.复杂度分析

时间复杂度:希尔排序的时间复杂度一般较为难计算,通过大量测验一般认为其时间复杂为O(N^1.3)。

空间复杂度:由于没有开辟额外的空间大小,因此空间复杂度为O(1)。

结束语

本篇博客是数据结构——排序 的第一篇。

感谢各位大佬能阅读本文。

求点赞收藏关注!!!十分感谢!!!

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

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

相关文章

tcp 网络通信及抓包工具的使用

tcp网络通信 本地回环&#xff08;Loopback&#xff09;的概念 本地回环地址是一个特殊的IP地址&#xff0c;用于指向计算机本身的网络接口。在IPv4中&#xff0c;最常见的本地回环地址是127.0.0.1&#xff0c;而在IPv6中则是::1。这个地址用于测试网络软件&#xff0c;确保网…

量化交易backtrader实践(四)_评价统计篇(1)_内置评价

背景 通过对基础的学习和不断深入的实践&#xff0c;当我们已经能够制作出快速获取数据&#xff0c;以及制作出多个股票 乘上多种策略进行回测的部分的时候&#xff0c;我们就会明显发现数据有点多了&#xff0c;比如10支股票都用了3种策略就得到30段数据&#xff0c;一页显示…

亲测好用,ChatGPT 3.5/4.0新手使用手册,最全论文指令手册~ 【2024年 更新】

本以为遥遥领先的GPT早就普及了&#xff0c;但小伙伴寻找使用的热度一直高居不下&#xff0c;其实现在很简单了&#xff01; 国产大模型快200家了&#xff0c;还有很多成熟的国内AI产品&#xff0c;跟官网一样使用&#xff0c;还更加好用~ ① 3.5 大多数场景是够用的&#xff…

Mix|使用VS2017CMake构建Qt工程 仿照MVS(仅用于学习)

MVS下载链接&#xff1a;https://www.hikrobotics.com/cn/machinevision/service/download/?module0 CMake工程构建参考&#xff1a;CMake|VS2017CMake3.8搭建Qt项目 文章目录 效果图整体结构实现代码最外层CMakeLists.txt代码实现及CMakeLists.txt搭建CMakeLists.txt搭建主函…

[创业之路-141] :产品经理 - NPDP概述

目录 一、产品经理以及主要职责 1.1 概述 1、市场调研与需求分析 2、产品规划与设计 3、项目管理与协调 4、产品推广与销售支持 5、产品运营与维护 6、其他职责 1.2 产品经理与项目经理的职责分工 1.2.1 职责区别 产品经理 项目经理 1.2.2 合作方式 二、什么是NP…

EXCEL——Vlookup17个高级用法

大纲 一、基本语法 1、参数详解 二、入门篇 1、单条件查找 2、屏蔽查找返回的错误值 三、进阶篇 1、反向查找 2、包含查找 3、区间查找 4、含通配符查找 5、多列查找 6、多区域查找 四、高级篇 1、多条件查找 2、合并单元格查找 3、带合并单元格的多条件查找 …

[数据集][目标检测]夜间老鼠检测数据集VOC+YOLO格式316张1类别+视频文件1个

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;316 标注数量(xml文件个数)&#xff1a;316 标注数量(txt文件个数)&#xff1a;316 标注类别…

MATLAB进阶:矩阵代数

今天我们学习矩阵在MATLAB中的运算。 运算符 与数组运算相同&#xff1a; A. ’转罝 A’&#xff08;共轭&#xff09;转罝 共轭转置&#xff08;A’或A†&#xff09;&#xff1a; 对于一个复数矩阵A&#xff0c;其共轭转置记作A’或A†。共轭转置不仅将矩阵A的行和列互…

大话C语言:第46篇 C语言项目工程化之Makefile详解

1 Makefile概述 Makefile是一种用于自动化构建和管理程序的工具&#xff0c;以文本文件的形式存在。它主要记录了程序的编译规则、依赖关系和操作指令&#xff0c;使得在开发过程中能够轻松地进行代码的编译、链接和部署。 Makefile文件中的命令有一定规范&#xff0c;一旦该文…

Unity--XLua调用C#

Unity–XLua调用C# 由于Unity/C# 和lua是两种语言&#xff0c;两种语言的特性不一样&#xff0c;因此&#xff0c;如果要互相调用的话&#xff0c;需要第三方作桥梁. 因此&#xff0c;为了在Unity中/C#中使用lua的特性&#xff0c;需要在Unity中安装插件&#xff0c;Xlua/toLu…

【学习笔记】8、脉冲波形的变换与产生

本章简略记录。 8.1 单稳态触发器&#xff08;脉冲触发&#xff09; 单稳态触发器 应用于 &#xff1a;&#xff08;1&#xff09;脉冲整型&#xff08;2&#xff09;脉冲延时 &#xff08;3&#xff09;定时 单稳态触发器的工作特性&#xff1a; 没有触发脉冲作用时&#xf…

Flink入门(五)--Flink算子

Map DataStream → DataStream 一个接受一个元素并产生一个元素的函数。 示例 dataStream.map { x > x * 2 } FlatMap DataStream → DataStream 一个接受一个元素并产生零个、一个或多个元素的函数。 例如 dataStream.flatMap { str > str.split(" ") }…

besier打断和升阶,高阶性质

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 问题描述 对besier曲线在u处打断&#xff0c;生成两条besier曲线对besier曲线升阶处理 bezier高阶性质 求导推导 P ( t ) ∑ i 0 n B i n ( t ) b i \boldsymbol …

Python 爬虫入门(十二):正则表达式「详细介绍」

Python 爬虫入门&#xff08;十二&#xff09;&#xff1a;正则表达式 前言一、正则表达式的用途二、正则表达式的基本组成元素2.1 特殊字符2.2 量词2.3 位置锚点2.4 断言2.5 字符集2.6 字符类2.6.1 基本字符类2.6.2 常见字符类简写2.6.3 POSIX字符类2.6.4 组合使用 三、 正则表…

Datawhale X 李宏毅苹果书 AI夏令营 学习笔记(二)

自适应学习率 我们梯度下降在参数更新上&#xff0c;公式是 W t W t − 1 − η g t &#xff0c; η 是学习率&#xff0c; g t 是梯度 W_tW_{t-1}-\eta g_t&#xff0c;\eta是学习率&#xff0c;g_t是梯度 Wt​Wt−1​−ηgt​&#xff0c;η是学习率&#xff0c;gt​是梯度…

03_React 收集表单数据和 组件生命周期

React 收集表单数据和 组件生命周期 一、收集表单数据1、例子1.1 需求&#xff1a;定义一个包含表单的组件&#xff0c;输入用户名密码后&#xff0c;点击登录提示输入信息 2、理解&#xff1a;包含表单的组件分类2.1 受控组件2.2 非受控组件 二、高阶函数\_函数柯里化1、复习-…

9 正则表达式:Java爬虫和正则表达式、String中的正则表达式方法(基本语法7)

文章目录 前言一、正则表达式1 [ ] 语法(1)[ABC] 和 [^ABC](2)[A-Z]和[a-zA-Z]小总结2 特殊字符语法(\w 这些)3 数量符4 \ 、()、 |5 锚点 ^ 和 $,\b,\B6 (?i) : 忽略其后面的大小写 ---- 这个Java是可以的,其他语言我不知道(正则表达式虽然大多通用,但也有部分是…

zabbix5.0与7.0版本区别 切换建议

Zabbix5.0和Zabbix7.0的区别 1. 性能和扩展性优化 1.1 高效的数据处理和存储 优化的数据库性能&#xff1a; Zabbix 7.0 在数据库层面进行了多项优化&#xff0c;以减少查询延迟和提高数据处理速度。这包括对数据库结构的改进和索引优化&#xff0c;使得大规模数据的读取和写…

Spark-driver和executor启动过程

一、上下文 《Spark-SparkSubmit详细过程》详细分析了从脚本提交任务后driver是如何调用到自己编写的Spark代码的&#xff0c;而我们的Spark代码在运行前必须准备好分布式资源&#xff0c;接下来我们就分析下资源是如何分配的 二、Spark代码示例 我们以一个简单的WordCount程…

打卡学习Python爬虫第五天|Xpath解析的使用

什么是Xpath&#xff1f;是在XML文档中搜索内容的一门语言&#xff0c;HTML可以看作是xml的一个子集。 目录 1、安装lxml模块 2、导入lxml中的etree子模块 3、Xpath使用方法 3.1.选择节点 3.2.选择属性 3.3.选择文本内容 3.4.使用通配符*过滤节点 3.5.使用中括号[]索引…