【六大排序详解】中篇 :选择排序 与 堆排序

选择排序 与 堆排序

选择排序

  • 选择排序 与 堆排序
    • 1 选择排序
      • 1.1 选择排序原理
      • 1.2 排序步骤
      • 1.3 代码实现
    • 2 堆排序
      • 2.1 堆排序原理
        • 2.1.1 大堆与小堆
        • 2.1.2 向上调整算法
        • 2.1.3 向下调整算法
      • 2.2 排序步骤
      • 2.3 代码实现
    • 3 时间复杂度分析
  • Thanks♪(・ω・)ノ
    • 下一篇文章见!!!

1 选择排序

1.1 选择排序原理

选择排序可以用扑克牌理解,眼睛看一遍所有牌,选择最小的放在最左边。然后略过刚才排完的那张,继续进行至扑克牌有序。这样一次一次的挑选,思路很顺畅。总结为:
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。
在这里插入图片描述

1.2 排序步骤

  1. 从头开始遍历数组,设置mini指向最小值下标(先指向首元素)。
  2. 遇到比a[mini]小的值,mini改变为新下标。直到遍历到结尾。
  3. 将数组首元素与mini指代元素交换位置。
  4. 从排好序的下一个元素开始,重复 1-3 步骤。
  5. 直到排序完成。
    在这里插入图片描述

1.3 代码实现

void SelectSort(int* a, int n) {
	int begin = 0;//无序部分开头
	int mini = 0;//先设置mini指向开头
  //直到begin = n ,都有序。
	while (begin<n) {
  		//从无序部分开始,选择最小值。
		for (int i = begin; i < n; i++) {
			if (a[i] < a[mini]) //如果小则下标更新。
				mini = i;
		}
		//将选择出来的最小值放置到无序部分开头。
		swap(a, begin, mini);
		begin++;//begin向后推进
		mini = begin;//mini更新
	}
}

排序功能实现,效果非常棒!
排序结果
直接选择排序的特性总结:

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

2 堆排序

2.1 堆排序原理

堆排序是一种特殊的选择排序,堆排序以二叉树为基础。选择两个子元素其一,然后逐层上升或下沉,直到有序。在认识理解堆排序之前,我们需要了解如何建堆。这里我们由于是学习堆排序,所以下面我只介绍堆的大小堆建立,向上调整算法,向下调整算法。其余堆相关知识会在另一篇文章详细介绍。

2.1.1 大堆与小堆

首先:堆 是一种特殊的树,满足以下条件即为堆,是二叉树的顺序结构。

在这里插入图片描述

二叉树内容见:二叉树解释
根据二叉树的知识,堆一定是完全二叉树(除了最后一层,其他层的节点个数都是满的,最后一层的节点都集中在左部连续位置)
堆分为大小堆
即 :堆中每一个节点的值都必须大于等于或小于等于其左右子节点的值
每个节点的值都大于等于其子树节点的堆叫“大堆“,根是所有数据的最大值
每个节点的值都小于等于其子树节点的堆叫“小堆“,根是所有数据的最小值

2.1.2 向上调整算法

我们如何把基本的数组变成大堆和小堆呢?这里就需要向上调整算法。
向上调整顾名思义,就是从尾部开始,一层一层向上调整。

以建大堆为例

  1. 从尾节点开始,如果该孩子节点大于父母节点,则向上调整(上浮)。
  2. 直到调整到合适的位置。
  3. 尾节点向前推移,继续重复 1 - 2 步骤。
  4. 直到遍历所有元素,完成建堆。
void adjustup(int* a, int child) ;
int main(){
//...
	for (int i = n - 1; i > 0; i--) {
		adjustup(a, i);//逐个遍历
	}
//...
}
//建堆


void adjustup(int* a, int child) {

	int parent = (child - 1) / 2;//根据二叉树知识取父母节点

	while (child > 0) {
		if (a[child] > a[parent]) {
			swap(a, child, parent);
			//如果该孩子节点大于父母节点,则向上调整(上浮)
		}

		child = parent;//孩子节点迭代
		parent = (child - 1) / 2;//父母节点迭代
	}

}

这样就建立了大堆。小堆原理相同,只需更改大于号和小于号。

2.1.3 向下调整算法

建立好堆之后,如何进行排序呢?这时就需要向下调整算法。首先我们要有一个共识:
排升序建大堆;排倒序建小堆。
之所以这样是因为向下调整算法的缘故,下面我们来看向下调整算法,之后解释原因。
以排升序为例

  1. 首先头元素与尾元素交换位置。
  2. 然后从头开始向下调整,如果父母节点大于孩子节点中较大的,则交换。
  3. 调整完成后,尾向前推进。继续重复 1 - 2 步骤。
  4. 直到遍历所有元素。
void adjustdown(int* a, int parent,int size);
int main(){
//...
	int end = n - 1;
	while (end > 0) {
		swap(a, end, 0);
		adjustdown(a,0,end);
		end--;
	}
//...
}
void adjustdown(int* a, int parent,int size) {
	int child = parent * 2 + 1;
	while (child < size) {
		if (child + 1 < size && a[child + 1] > a[child]) {
			child++;
		}

		if (a[child] > a[parent]) {
			swap(a, child, parent);
			
		}
		parent = child;
		child = parent * 2 + 1;
	}
	
}

这样遍历一遍向下调整就可以完成排序。我们理解向下调整算法之后就可以发现
**排升序建大堆;排倒序建小堆。**是非常巧妙的,
以排升序为例,每次放在交换到首元素 的都是最小值(最大值),然后向下调整,把它放到该放在的位置上。

2.2 排序步骤

我们理解上述两种算法之后,就可以非常顺畅理解堆排序。

  1. 向上调整建堆
  2. 向下调整排序
    在这里插入图片描述
    理解向上调整算法和向下调整算法之后,堆排序就迎刃而解。

2.3 代码实现

void adjustup(int* a, int child) {

	int parent = (child - 1) / 2;

	while (child > 0) {
		if (a[child] > a[parent]) {
			swap(a, child, parent);
		}

		child = parent;
		parent = (child - 1) / 2;
	}

}

void adjustdown(int* a, int parent,int size) {
	int child = parent * 2 + 1;
	while (child < size) {
		if (child + 1 < size && a[child + 1] > a[child]) {
			child++;
		}

		if (a[child] > a[parent]) {
			swap(a, child, parent);
			
		}
		parent = child;
		child = parent * 2 + 1;
	}
	
}
void HeapSort(int* a, int n) {
	assert(a);

	for (int i = n - 1; i > 0; i--) {
		adjustup(a, i);//逐个遍历
	}
	int end = n - 1;
	while (end > 0) {
		swap(a, end, 0);
		adjustdown(a,0,end);
		end--;
	}
}

这里我们是可以进一步优化的,因为向上调整算法可以有向下调整算法来代替。
算法优化就交给你完成了。

3 时间复杂度分析

让我们和之前的排序算法来比较一下。依然是10万组数据,让我们看一下运行时间。(以冒泡排序为对照)
在这里插入图片描述

显然选择排序和插入排序是一个级别,堆排序和希尔排序都非常快速。
我们再来比较一下希尔排序与堆排序。100万组数据
在这里插入图片描述
这里希尔貌似更快,但其实希尔排序与堆排序是一个量级,甚至在更多数据下,堆排序会更优。

Thanks♪(・ω・)ノ

下一篇文章见!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

你真的理解了阻塞和非阻塞、同步和异步吗?

阻塞和非阻塞是一种状态&#xff0c;关键要看调用线程有没有被挂起。以处理I/O为例&#xff0c;如果是调用线程处理阻塞型I/O&#xff0c;那么调用线程会被挂起&#xff0c;此时调用线程就是阻塞的&#xff1b;如果调用线程处理的是非阻塞I/O&#xff0c;调用线程开启了I/O之后…

【Spring】15 MessageSourceAware 接口

文章目录 1. 简介2. 功能3. 使用3.1 创建并实现接口3.2 配置 Bean 信息3.3 资源文件3.4 创建启动类3.5 启动 4. 应用场景总结 Spring 框架为开发者提供了丰富的扩展点&#xff0c;其中之一是 Bean 生命周期中的回调接口。本文将专注介绍一个与国际化相关的接口 MessageSourceAw…

运筹视角下,体系化学习机器学习算法原理的实践和总结

文章目录 引言目标设计目标实践文章汇总经验总结一则预告 引言 上两周总结了我在体系化学习运筹学基础知识方面的个人经验&#xff0c;看过那篇文章的人可能知道&#xff0c;今年我还花了很多时间学习机器学习中各种模型的算法原理。 在工业应用中&#xff0c;机器学习和运筹…

Spark中使用scala完成数据抽取任务 -- 总结

如题 任务二&#xff1a;离线数据处理&#xff0c;校赛题目需要使用spark框架将mysql数据库中ds_db01数据库的user_info表的内容抽取到Hive库的user_info表中&#xff0c;并且添加一个字段设置字段的格式 第二个任务和第一个的内容几乎一样。 在该任务中主要需要完成以下几个阶…

【python】python课设 天气预测数据分析及可视化(完整源码)

目录 1. 前言2. 项目结构3. 详细介绍3.1 main.py3.2 GetModel.py3.3 GetData.py3.4 ProcessData.py3.5天气网.html 4. 成果展示 1. 前言 本文介绍了天气预测数据分析及可视化的实现过程使用joblib导入模型和自定义模块GetModel获取模型&#xff0c;输出模型的MAE。使用pyechart…

鸿蒙应用开发 自定义下拉刷新动画

1 概述 属性动画&#xff0c;是最为基础的动画&#xff0c;其功能强大、使用场景多&#xff0c;应用范围较广。常用于如下场景中&#xff1a; 一、页面布局发生变化。例如添加、删除部分组件元素。二、页面元素的可见性和位置发生变化。例如显示或者隐藏部分元素&#xff0c;…

基于 Webpack 插件体系的 Mock 服务

背景 在软件研发流程中&#xff0c;对于前后端分离的架构体系而言&#xff0c;为了能够更快速、高效的实现功能的开发&#xff0c;研发团队通常来说会在产品原型阶段对前后端联调的数据接口进行结构设计及约定&#xff0c;进而可以分别同步进行对应功能的实现&#xff0c;提升研…

LINUX系统安装和管理

目录 一.应用程序 对比应用程序与系统命令的关系 典型应用程序的目录结构 常见的软件包装类型 二.RPM软件包管理 1.RPM是什么&#xff1f; 2.RPM命令的格式 查看已安装的软件包格式 查看未安装的软件包 3.RPM安装包从哪里来&#xff1f; 4.挂载的定义 挂载命令moun…

C语言蛇形矩阵

文章目录 每日一言题目解题思路全部代码结语 每日一言 山有榛&#xff0c;隰有苓。云谁之思&#xff1f;西方美人。 --邶风简兮 题目 解题思路 话不多说&#xff0c;直接看图 通过观察图表&#xff0c;我想到了这种方法&#xff1a; 我将数字放置的位置分为两大类&#xff…

Python深度学习028:神经网络模型太多,傻傻分不清?

文章目录 深度学习网络模型常见CNN网络深度学习网络模型 在深度学习领域,有许多常见的网络模型,每种模型都有其特定的应用和优势。以下是一些广泛使用的深度学习模型: 卷积神经网络(CNN): 应用:主要用于图像处理,如图像分类、物体检测。 特点:利用卷积层来提取图像特…

【UML】第12篇 序列图(1/2)——基本概念和构成

目录 一、什么是序列图&#xff08;Sequence Diagram&#xff09; 1.1 定义 1.2 主要用途 1.3 序列图和BPMN的区别和联系 二、序列图的构成 2.1 对象 2.2 生命线 2.3 消息 2.4 激活 序列图&#xff0c;是我个人认为的用处最多的一种图。产品和研发的同学&#xff0c;都…

WorkPlus一站式协同解决方案,助力企业降本增效

在企业数字化转型的过程中&#xff0c;很多企业都会遇到一个共同问题&#xff1a;重复建设基础功能&#xff0c;耗费大量时间和资源。为解决这一难题&#xff0c;WorkPlus已经将一些通用、基础且有技术门槛的功能进行了集成与开发&#xff0c;如IM&#xff08;即时通讯&#xf…

截断整型提升算数转换

文章目录 &#x1f680;前言&#x1f680;截断&#x1f680;整型提升✈️整型提升是怎样的 &#x1f680;算术转换 &#x1f680;前言 大家好啊&#xff01;这里阿辉补一下前面操作符遗漏的地方——截断、整型提升和算数转换 看这一篇要先会前面阿辉讲的数据的存储否则可能看不…

“C语言“——scanf()、getchar() 、putchar()、之间的关系

scanf函数说明 scanf函数是对来自于标准输入流的输入数据作格式转换&#xff0c;并将转换结果保存至format后面的实参所指向的对象。 而const char*format 指向的字符串为格式控制字符串&#xff0c;它指定了可输入的字符串以及赋值时转换方法。 简单来说给一个打印格式(输入…

css radial-gradient 径向渐变基本语法与使用

在之前的文章《深入理解Css linear-gradient线性渐变》我们了解了CSS中的线性渐变&#xff0c;本文将介绍CSS中的另一种渐变———径向渐变&#xff08;Radial Gradient&#xff09;&#xff1a; CSS中的径向渐变&#xff08;Radial Gradient&#xff09;允许你创建从一个颜色…

基于Java SSM框架实现交通事故档案管理系统项目【项目源码+论文说明】

基于java的SSM框架实现交通事故档案管理系统演示 摘要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于交通事故档案管理系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0…

企业知识库:从信息管理到知识创新的转变

在当今这个信息爆炸的时代&#xff0c;企业知识库的建设已经成为了企业持续发展的重要基石。从传统的信息管理到现代的知识创新&#xff0c;企业知识库的角色和功能也在不断地演变和升级。本文将探讨企业知识库的发展历程&#xff0c;以及如何实现从信息管理到知识创新的转变。…

谷歌Gemini演示视频解析

在刚刚过去的前两天 谷歌发布了号称最强的多模态大模型Gemini 不仅提供了Ultra、Pro 和 Nano版本 而且在32项学术基准中 Gemini Ultra都达到了SOTA水平 甚至在MMLU测试中 Gemini Ultra 的得分率高达 90.0%&#xff0c; 是第一个超过人类专家的模型 应该说&#xff0c;G…

【vtkWidgetRepresentation】第十八期 vtkHoverWidget

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 前言 本文分享vtkHoverWidget,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO 1. vtkHoverWidget vtkHoverWidget用于在呈现窗口中…

如何在飞书自建项目中接入ChatGPT打造智能问答助手并远程访问

文章目录 前言环境列表1.飞书设置2.克隆feishu-chatgpt项目3.配置config.yaml文件4.运行feishu-chatgpt项目5.安装cpolar内网穿透6.固定公网地址7.机器人权限配置8.创建版本9.创建测试企业10. 机器人测试 前言 在飞书中创建chatGPT机器人并且对话&#xff0c;在下面操作步骤中…