【排序算法】第二章:选择排序----普通选择排序与堆排序的详解和对比

server/2024/9/22 18:55:16/

🫡和我一起感受 两种算法>排序算法的魅力吧!
在这里插入图片描述

前言:本文可能稍微涉及到一点其他算法>排序算法,若想要了解可以看看:第一章:插入排序`




一、普通选择排序


注意下面几种写法的 Max 和 Min 指的都是 元素下标,不是元素本身的值



1.1.一般写法

这种写法,我认为是最好理解的 算法>排序算法了,直接暴力遍历找最值交换即可,思路很直接

void SelectSort(int* a, int n) {for (int i = 0; i < n - 1; ++i) {int Min = i;for (int j = i + 1; j < n; ++j) {if (a[Min] > a[j]) Min = j;}Swap(&a[i], &a[Min]);}
}


1.2.一般写法动图演示

在这里插入图片描述



2.1.优化版本

优化思路:既然每一轮都要遍历数组,可以直接将 最大值和最小值 一起找出来,不用像一般写法:每一轮只找出一个 最值,效率较低

每轮找到最大值 和 最小值 就和 头尾交换即可

2.2.优化版本的错误写法
void SelectSort2(int* a, int n) {int begin = 0, end = n - 1;while (begin < end) {int Min = begin, Max = end;for (int i = begin + 1; i <= end; ++i) {if (a[i] < a[Min]) Min = i;if (a[i] > a[Max]) Max = i;}Swap(&a[end], &a[Max]);Swap(&a[begin], &a[Min]);begin++;end--;Print(a, 10);}
}

比如下面这种情况

注意: Max 和 Min 指的都是 元素下标,不是元素本身的值

遍历一遍记录最值下标是 Max == 8, Min == 9

在这里插入图片描述

若 直接交换:Swap(a[Max], a[end]) : {2, 5, 8, 1, 4, 7, 3, 6, 0, 9}

可以发现,原本的 Min == 9 的位置,数值变成了 a[Min] = 9 ??!

此时若直接进行:Swap(a[Min], a[begin]) 必然会出错

⭐原因:Min 的位置 和 Max 要交换的目标位置 end 重叠了

⭐方案:加一个 if 语句判断:若 Min == end 则 Min = Max 代表 Min 变成 Max 交换后的位置,就是原有的 Min

if(Min == end) {Min = Max;
}

反过来一样:

若先进行 Swap(a[Min], a[begin]); 则 要判断

if(Max == begin) {Max = Min;
}
2.3.优化版本的正确写法:
void SelectSort2(int* a, int n) {int begin = 0, end = n - 1;while (begin < end) {int Min = begin, Max = end;for (int i = begin + 1; i <= end; ++i) {if (a[i] < a[Min]) Min = i;if (a[i] > a[Max]) Max = i;}Swap(&a[end], &a[Max]);if(Min == end) {Min = Max;}Swap(&a[begin], &a[Min]);begin++;end--;Print(a, 10);}
}


3.1.一般的选择排序 和 优化后选择排序(以及其他几种排序)的性能对比

下面我用代码随机生成 100000 个数据,执行几个算法>排序算法,显示的数据为 时间,以 ms 为单位

在这里插入图片描述

可以看到 一般选择排序 和 优化选择排序的在 十万数据量的差别较小,但是还是有一定优化的

(另外说一下:冒泡“真强”,希尔真强 doge)


直接选择排序的特性总结:

  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定



二、堆排序

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种算法>排序算法它是选择排序的一种。它是 通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。

在这里插入图片描述

想要升序排列,应该使用 大根堆
而不是常理所想的小根堆:常理中,想要升序,top 应该是 最小值才对,但是….
若使用小根堆 的堆排序
每轮排序出 top 为 最小值,和 最后一个元素交换:
依此类推 从后往前的序列元素分别是: 第 n 小的、第 n-1 小的 … 第 2 小的、第 1 小的
反而是 降序排序
因此,依照堆排序的原理,排序应该反着来
// 堆排序void HeapSort(HPDataType* a, int n)
{// 建堆// 因为 Parent = (child - 1) / 2// 需要从最后一个节点的父节点开始向前循环比较// 最后一个节点的下标是 (n-1)// 则 最后一个节点的父节点 是 (  (n - 1)   - 1) / 2for (int i = ((n - 1) - 1) / 2; i >= 0; --i) {AdjustDown(a, n, i);}// 单独一个元素向下调整时间复杂度为 O(logN)  一共调整约 N 轮,时间复杂度为 O(NlogN)int end = n-1;while (end > 0) {Swap(&a[0], &a[end]);   // 把处在第一位置的最优值 和 最后位置的值替换AdjustDown(a, end, 0);  // 再 将 a[0] 进行向下调整end--;}
}void AdjustDown(HPDataType* a, int size, int Parent) {// 孩子取较大的那个: 为了结果为 升序排列int LeftChild = Parent * 2 + 1;int RightChild = Parent * 2 + 2;int child = (RightChild < size && a[LeftChild] < a[RightChild]) ? RightChild : LeftChild;// 注意:这里必须加上:RightChild < size :因为 若 RightChild 越界,越界的值是随机的,也是可能大于另一方的,也可能被计算进去,// 当 child 为越界下标时,下面的循环就不能进去,导致较小的但是合法的 左孩子LeftChild 没有参与向下调整,则会漏掉一个值没有计算while (child < size && a[child] > a[Parent]) {  //  只要孩子 :child < size 说明孩子的下标就是合法的Swap(&a[child], &a[Parent]);Parent = child;LeftChild = Parent * 2 + 1;RightChild = Parent * 2 + 2;child = (RightChild < size && a[LeftChild] < a[RightChild]) ? RightChild : LeftChild;}
}

堆排序的特性总结:

  1. 堆排序使用堆来选数,效率就高了很多。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定



三、选择排序堆排序 的性能对比

下面我用代码随机生成 100000 个数据,执行两个算法>排序算法,显示的数据为 时间,以 ms 为单位
在这里插入图片描述

可以见识到 堆排序 O(nlogn) 的威力了吧,(薄纱 O(n^2))




四、浅谈 各种算法>排序算法的意义

上一章节讲解的 希尔排序 中提到

希尔排序时间复杂度大概在 O ( n 1.3 ) O(n^{1.3}) O(n1.3) 略大于 O ( n l o g n ) O(nlogn) O(nlogn)

我们在此 将 希尔排序 ( O ( n 1.3 ) O(n^{1.3}) O(n1.3) ) 和 堆排序 O ( n l o g n ) O(nlogn) O(nlogn)) 对比一下

十万 数据量级在这里插入图片描述

千万 数据量级在这里插入图片描述

一亿 数据量级在这里插入图片描述

希尔排序 在后面数据量级升级后,反超了 堆排序,说明了一点

时间复杂度,看的是最坏的情况,有时候不能用于真正的速度对比,算法的运行速度和 算法思想、数据的实际情况等等相关

同时要注意,我们堆排序之前,还要进行 O(n) 的建堆,我们也不能忽略 O(n) 的影响


⭐总结:每个排序都有其存在的意义,不能只盯着算法>排序算法的时间复杂度大小的比较,更多的是看适不适合当前实际的应用场景,,比如堆排序虽然时间复杂度较为优秀,但是,例如当数据基本有序时,使用 插入排序 都能秒了 堆排,不信?🤣 看下面

如图:我先用 希尔排序 将 数组 a2 排序成有序的,营造数据有序场景,来测试 插入排序 和 堆排序

在这里插入图片描述

😎😎谁更强一目了然!!




🫡至此,第二章完结!

【若文章有什么错误或则可以改进的地方,非常欢迎评论区讨论或私信指出】**


http://www.ppmy.cn/server/22561.html

相关文章

将mysql转为oracle

mysql->oracle 前言 今天的任务是把用mysql数据库编写的程序转成oracle,这也是我第一次用oracle可谓是错误百出啊。下载oracle?NO在公司我们不需要本地下载oracle&#xff0c;&#xff08;如果你是想自己学习当我没说&#xff0c;不魔法下载很慢&#xff0c;有时间我会写一…

请编写函数fun,该函数的功能是:实现B=A+A‘,即把矩阵A加上A的转置,存放在矩阵B中。计算结果在main函数中输出。

本文收录于专栏:算法之翼 https://blog.csdn.net/weixin_52908342/category_10943144.html 订阅后本专栏全部文章可见。 本文含有题目的题干、解题思路、解题思路、解题代码、代码解析。本文分别包含C语言、C++、Java、Python四种语言的解法完整代码和详细的解析。 题干 请编…

【.net core】【sqlsugar】条件查询时使用Contains的注意问题

在使用sqlsugar条件查询时&#xff0c;条件中如果使用Contains&#xff0c;如果需要自定义条件&#xff0c;需保证类型一致 目标需求&#xff1a;生成 WHERE ([1],[2],[3] like concat(%, concat( concat( [, CAST(orderState AS CHAR)) , ]) ,%)) 为条件的查询语句 //…

Django项目之电商购物商城 -- 校验用户输入密码是否合法

Django项目之电商购物商城 – 校验用户输入密码是否合法 需要开发文档和前端资料的可私聊 一. 创建用户逻辑操作 1. 创建用户app – users python manage.py startapp users2.注册app users.apps.UsersConfig,3. 创建视图 from django.shortcuts import render from djan…

分布式与一致性协议之CAP(三)

CAP ACID理论:CAP的"酸"&#xff0c;追求一致性。 提到ACID,它很容易理解&#xff0c;在单机上实现也不难&#xff0c;比如可以通过锁、时间序列等机制保障操作的顺序执行&#xff0c;让系统实现ACID特性。但是一说要实现分布式系统的ACID特性比较难实现呢&#xf…

Python高效修补Excel缺失数据实战指南

本文将详细介绍如何利用Python的Pandas库来识别并处理Excel文件中的缺失数据。我们将探讨几种常见的处理策略,包括删除、填充(单一插补和多重插补)、以及使用预测模型进行智能填补。通过实际代码示例,帮助读者掌握高效处理缺失值的方法,以确保数据分析的准确性和完整性。 …

预测房屋价格(使用SGDRegressor随机梯度下降回归)

线性回归&#xff1a;预测未来趋势01&#xff08;预测房屋价格&#xff09; 文章目录 线性回归&#xff1a;预测未来趋势01&#xff08;预测房屋价格&#xff09;前言一、案例介绍&#xff1a;二、架构图&#xff1a;&#xff08;流程图&#xff09;三、使用了什么技术&#xf…

Golang实现一个批量自动化执行树莓派指令的软件(1)文本加密配置命令行交互实现

简介 实现一个在配置文件设置信息&#xff0c;一运行就可以自动执行设定指令的软件。 这次实现的是 &#xff1a; 1. 加密解密模块&#xff0c; 用于加密密码&#xff0c; 在配置时配置已加密的密码就可以&#xff1b; 2. 需要配置&#xff0c;自然也就有配置文件的序列化反序列…