【数据结构入门】排序算法之插入排序与选择排序

news2024/9/19 10:57:14

目录

前言

一、排序的概念及运用

1.排序的概念

2.排序的运用

3.常见排序算法

二、插入排序与选择排序

2.1插入排序

2.1.1直接插入排序

1)基本思想

2)具体步骤

3)算法特性

4)算法实现

2.1.2希尔排序

1)  基本思想

2)具体步骤

3)算法特性

4)算法实现

2.2选择排序

2.2.1直接选择排序

1)  基本思想

2)具体步骤

3)算法特性

4)算法实现

 2.2.2 堆排序

1)  基本思想

2)具体步骤

3)算法特性

4)算法实现

三、算法复杂度及稳定性分析

总结


前言

在数据结构中,排序是指将一组数据按照特定的规则重新排序的过程。排序可以使数据按照升序或者降序排列,从而方便后续的操作和查找。

一、排序的概念及运用

1.排序的概念

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

例如:

内部排序:数据元素全部放在内存中的排序。

外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

2.排序的运用

排序在生活中的使用无处不在,成绩排名、商品排名、电影榜单等等数不胜数。

 

3.常见排序算法

 

二、插入排序与选择排序

2.1插入排序

2.1.1直接插入排序

1)基本思想

直接插入排序是一种简单的排序算法,它的基本思想是将待排序的数据分成已排序和未排序两部分,每次从未排序部分中取出一个元素,然后将其有序地插入到已排序部分的合适位置。

当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移

动画演示:

2)具体步骤
  1. 将第一个元素视为已排序部分,将剩余的元素视为未排序部分。

  2. 从未排序部分取出第一个元素,将其插入到已排序部分的合适位置。插入时,从后往前逐个比较已排序部分的元素,将大于待插入元素的元素依次后移,直到找到一个小于或等于待插入元素的位置。

  3. 重复步骤2,直到未排序部分中的所有元素都插入到已排序部分。

3)算法特性
  1. 元素集合越接近有序,直接插入排序算法的时间效率越高

  2. 时间复杂度:直接插入排序的时间复杂度是O(n^2),其中n是待排序数据的个数。当输入数据已经基本有序时,直接插入排序的性能较好,时间复杂度可以降低到O(n)。但当输入数据完全逆序时,直接插入排序的性能较差,时间复杂度会达到最大值O(n^2)。

  3. 空间复杂度:O(1),它是一种稳定的排序算法

  4. 稳定性:稳定

4)算法实现
void InsertSort(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		int end = i-1;
		int tmp = a[i];
		//将tmp插入到[0, end]区间中,保持有序
		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				--end;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}	
}

2.1.2希尔排序

希尔排序法又称缩小增量法。

1)  基本思想

将待排序的数据按照一定的增量分组,对每组数据进行插入排序,然后逐渐减小增量,重复上述步骤,直至增量为1,完成最后一轮的插入排序。

图示:

2)具体步骤
  1. 选择一个增量序列,常用的增量序列是希尔增量(N/2, N/4, N/8...,直到增量为1)。

  2. 对于每个增量,以增量作为间隔将待排序的数据分成多个组,分别对每个组进行插入排序。

  3. 逐渐减小增量,重复上述步骤,直至增量为1。

3)算法特性
  1. 希尔排序是对直接插入排序的优化。

  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。

  3. 希尔排序的时间复杂度较为复杂,最好情况下为O(n log^2 n),最坏情况下为O(n^2),平均情况下为O(n log n)。希尔排序的性能优于直接插入排序,尤其是对于数据量较大的情况,其性能优势更加明显。这里一般可以认为时间复杂度为O(N^1.3)左右。

  4. 稳定性:不稳定

4)算法实现

多组同时排法:

void ShellSort(int* a, int n)
{
	//预处理
	int gap = n;
	while (gap > 1)
	{
		//这里必须保证gap最后一次是1
		//gap /= 2;
		gap = gap / 3 + 1;

		for (int i = gap; i < n; i++)
		{
			int end = i - gap;
			int tmp = a[end + gap];

			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}	
}

 排完一组再排下一组:

void ShellSort(int* a, int n)
{
	//预处理
	int gap = n;
	while (gap > 1)
	{
		//这里必须保证gap最后一次是1
		//gap /= 2;
		gap = gap / 3 + 1;

		for (int j = 0; j < gap; j++)
		{
			for (int i = gap+j; i < n; i += gap)
			{
				int end = i - gap;
				int tmp = a[end + gap];

				while (end >= 0)
				{
					if (tmp < a[end])
					{
						a[end + gap] = a[end];
						end -= gap;
					}
					else
					{
						break;
					}
				}
				a[end + gap] = tmp;
			}
		}
	
	}	
}

2.2选择排序

2.2.1直接选择排序

1)  基本思想

每一次从待排序的数据中选择最小(或最大)的元素,放到已排序序列的末尾,直到全部待排序的数据元素排完 。

动画演示:

2)具体步骤
  1. 找到待排序序列中最小(或最大)的元素,记为A。

  2. 将A与待排序序列的第一个元素交换位置。

  3. 然后在剩余的待排序序列中找到最小(或最大)的元素,再与待排序序列的第二个元素交换位置。

  4. 重复上述步骤,直到待排序序列变为空。

3)算法特性
  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用

  2. 时间复杂度:直接选择排序的时间复杂度是O(n^2),无论输入数据的情况如何,都需要进行n-1次的比较和若干次元素交换。虽然直接选择排序的时间复杂度较高,但是它的优点是原地排序,不需要额外的空间。

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

  4. 稳定性:不稳定

4)算法实现
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}


void SelectSort(int* a, int n)
{
	int left = 0, right = n - 1;

	while (left < right)
	{
		int mini = left, maxi = left;
		for (int i = left + 1; i <= right; i++)
		{
			if (a[i] < a[mini])
			{
				mini = i;
			}
			if (a[i] > a[maxi])
			{
				maxi = i;
			}
		}
		Swap(&a[left], &a[mini]);
		//如果left和maxi重叠,交换后修正一下,否则这里会出问题,换两次换回去了
		if (maxi == left)
		{
			maxi = mini;
		}

		Swap(&a[right], &a[maxi]);

		left++;
		right--;
	}
}

 2.2.2 堆排序

堆排序是一种利用堆的数据结构进行排序的算法。堆是一种特殊的二叉树,满足堆性质:任意节点的值都大于等于(或小于等于)其子节点的值。需要注意的是排升序要建大堆,排降序建小堆。

1)  基本思想

将待排序的数据构造成一个堆,然后将堆顶元素与末尾元素交换,再对剩余的元素重新构造堆,以此类推,最终得到有序的序列。

2)具体步骤
  1. 构建最大堆(或最小堆),将待排序的数据转换为堆。

  2. 将堆顶元素(即最大值或最小值)与堆的最后一个叶子节点交换位置。

  3. 重新调整堆,将堆顶元素下沉,使得堆仍然满足堆性质。

  4. 重复上述步骤,直到堆只剩一个元素或为空。

3)算法特性
  1. 堆排序使用堆来选数,效率就高了很多。

  2. 时间复杂度:堆排序的时间复杂度是O(nlogn),堆的构建需要O(n)的时间,每次调整堆的时间为O(logn)。堆排序是一种原地排序算法,不需要额外的空间。由于堆排序具有良好的局部性,适合用于大规模数据的排序。

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

  4. 稳定性:堆排序是一种不稳定的排序算法,相同元素的相对位置可能会改变。

4)算法实现
void ADjustDown(int* a, int sz, int parent)
{
	int child = parent * 2 + 1;
	while (child < sz)
	{
		//选出左右孩子中大的一个
		//这里child+1的判断在前,不要先访问再判断
		//这里a[child + 1] > a[child] 建大堆用>, 建小堆用<
		if (child + 1 < sz && a[child + 1] > a[child])
		{
			//这地方可能会越界
			++child;
		}
		//这里a[child] > a[parent] 建大堆用>, 建小堆用<
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void HeapSort(int* a, int sz)
{
	//1.建堆 -- 向上调整建堆   NlogN
	//左右子树必须是大堆/小堆
	/*for (int i = 1; i < sz; i++)
	{
		ADjustUp(a, i);
	}*/

	//2.向下调整建堆  N
	//左右子树必须是大堆/小堆
	for (int i = (sz - 1 - 1) / 2; i >= 0; i--)
	{
		ADjustDown(a, sz, i);
	}

	int end = sz - 1;
	while (end > 0)
	{
		Swap(&a[end], &a[0]);
		ADjustDown(a, end, 0);
		--end;
	}
}

堆排序在前文中已经详细介绍了,具体见: 

【数据结构入门】二叉树之堆排序及链式二叉树

三、算法复杂度及稳定性分析

 

总结

        排序算法的选择可以根据数据的特点、数据量以及排序的要求来确定。不同的排序算法具有不同的时间复杂度和空间复杂度,因此在实际应用中需要根据具体情况选择合适的排序算法。

直接插入排序:

  • 直接插入排序是一种简单直观的排序算法,其思想是将待排序的序列分为已排序和未排序两部分,每次从未排序部分选择一个元素插入到已排序部分的合适位置,直到所有元素都插入到已排序部分为止。
  • 直接插入排序的时间复杂度为O(n^2),是稳定的排序算法。

希尔排序:

  • 希尔排序是直接插入排序的一种改进算法,其核心思想是通过多次分组插入排序,每次对间隔较远的元素进行插入排序,逐步缩小间隔直到间隔为1,最后进行一次完整的插入排序。
  • 希尔排序的时间复杂度取决于间隔序列的选择,一般为O(nlogn),是不稳定的排序算法。

直接选择排序:

  • 直接选择排序是一种简单直观的排序算法,其思想是每次从未排序的序列中选择最小(或最大)的元素,将其与未排序部分的第一个元素交换位置,直到所有元素都排序完成。
  • 直接选择排序的时间复杂度为O(n^2),是不稳定的排序算法。

堆排序:

  • 堆排序利用二叉堆这种数据结构进行排序,将待排序的元素依次插入到堆中,然后从堆顶依次取出最大(或最小)的元素,直到所有元素都取出为止。
  • 堆排序的时间复杂度为O(nlogn),是不稳定的排序算法。

 

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

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

相关文章

从苹果智能看端上大模型应用

将生成式人工智能集成到边缘设备本身就是一个重大挑战&#xff0c;我们需要在智能手机和计算机有限的计算能力和内存范围内高效地运行高级模型。确保这些模型运行迅速&#xff0c;而不会耗尽电池寿命或使设备过热&#xff0c;端上的局限增加了大模型应用的复杂性。此外&#xf…

LeetCode 算法:杨辉三角 c++

原题链接&#x1f517;&#xff1a;杨辉三角难度&#xff1a;简单⭐️ 题目 给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1]…

关于异常断电后jmeter的jmx文件异常变成二进制文件并成功恢复的心酸历程

今日下午正在编写jmeter接口&#xff0c;正在调试中&#xff0c;突然断电&#xff08;由于四川高温&#xff0c;导致电力紧缺的很&#xff09;&#xff0c;来电了后我正常启动电脑&#xff0c;打开后&#xff0c;赶紧打开jmeter&#xff0c;并打开最近打开的文件&#xff0c;我…

『功能项目』怪物受击的动画事件【10】

我们打开上一篇09着色器光透魔法球的项目&#xff0c; 本章要做的事情是在场景中创建一个怪物对象&#xff0c;当怪物被主角的魔法球击中后播放受击动画效果&#xff0c;此类技术用到动画事件帧&#xff0c;在动画上创建脚本。 首先打开资源商店选择一个免费资源的怪物模型加载…

静态ISP代理IP适合什么应用场景?

随着互联网的普及和发展&#xff0c;越来越多的设备和应用需要接入网络。在这些情况下&#xff0c;动态主机配置协议 (DHCP) 成为一种常见的选择&#xff0c;因为它会自动为客户端分配 IP 地址。然而&#xff0c;在某些特定场景下&#xff0c;静态 IP 地址 (ISP) 可能是更好的选…

哈夫曼树例题

从这道题可以看出需要构建哈夫曼树 构造哈夫曼树如下 9和5最小&#xff0c;构成左右树&#xff0c;二者之和是14&#xff0c;放入数组&#xff0c;5和9从数组去掉 14和16最小&#xff0c;构成左右树&#xff0c;二者之和是30&#xff0c;放入数组&#xff0c;14和16从数组去…

基于yolov8的玻璃瓶塑料瓶检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的玻璃瓶塑料瓶检测系统是一个利用深度学习技术的先进解决方案&#xff0c;专注于对图像、视频或实时摄像头流中的玻璃瓶和塑料瓶进行快速准确的检测与定位。该系统通过YOLOv8这一高效的目标检测算法&#xff0c;能够在多种应用场景下展现卓越的性能。…

Java面试题·解释题

系列文章目录 总章 Java解释题 文章目录 系列文章目录前言面向对象编程是什么&#xff1f;Java的跨平台原理Java的安全性Java的三大版本Java开发运行过程什么是JVM&#xff1f;什么是JDK&#xff1f;什么是JRE&#xff1f;JDK,JRE,JVM三者关系概括Java的数据类型&#xff1f;J…

有没有比较好用的在线翻译工具?实力推荐这4款。

当我们面对外文资料时&#xff0c;可能需要翻阅厚重的词典&#xff0c;耗费大量的时间和精力。在翻译这方面&#xff0c;很多人都十分依赖翻译工具的&#xff0c;因为这些工具只需几秒钟就能给出翻译结果&#xff0c;提高了我们的学习和工作的效率。但是随着翻译工具越来阅读&a…

灵感枯竭?ChatGPT助你轻松完成开题报告!

在学术探索的征途中&#xff0c;撰写论文开题报告是一项至关重要的初步工作。这项工作不仅标志着您研究旅程的起点&#xff0c;也是展现您研究思路和方法论的关键时刻。ChatGPT&#xff0c;作为一款前沿的人工智能工具&#xff0c;将以其深度学习能力&#xff0c;成为您学术探索…

【大模型系列篇】预训练模型:BERT GPT

2018 年&#xff0c;Google 首次推出 BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;。该模型是在大量文本语料库上结合无监督和监督学习进行训练的。 BERT 的目标是创建一种语言模型&#xff0c;可以理解句子中单词的上下文和含义&…

数学问题+贪心

前言&#xff1a;一开始我想的就是我每次都设置一个cnt记录每次增加量&#xff0c;后面都增加这个量&#xff0c;但是发现我忘记要把这个量先加到后面的元素上面去 正确的做法就是每次 a [ i ] > a [ i − 1 ] a[ i ] > a[ i-1 ] a[i]>a[i−1] 的时候再进行处理&…

量化交易backtrader实践(四)_评价统计篇(3)_更多评价与可视化

前情回顾 在第(1)节中&#xff0c;我们对于backtrader内置的评价指标一个接一个的进行了实践测试&#xff0c;其中第10个是金融投资组合分析库 - pyfolio&#xff0c;当时我们先去实践了Analyzer中的pyfolio指标&#xff08;其实就是03&#xff0c;07&#xff0c;08&#xff0…

Python TensorFlow入门与实践

摘要 本文旨在详细介绍如何使用Python和TensorFlow进行基本的深度学习任务。我们将从安装TensorFlow开始&#xff0c;逐步讲解如何创建简单的神经网络模型&#xff0c;并通过一个具体的示例来演示如何训练模型。此外&#xff0c;我们还将讨论一些高级主题&#xff0c;如卷积神…

“长风破浪”计划创作营圆满结营 9月10日“2024华韵视听大会”佛山见

8月26日&#xff0c;“长风破浪”青年音乐人唱享计划与青年影视人扶持计划创作营在佛山高明皂幕山上圆满结营。首季创作营历时一周&#xff0c;在高强度的创作中&#xff0c;20位青年影视人和音乐人群策群力&#xff0c;超额完成任务&#xff0c;共创两首歌曲、一部微短剧及一部…

Wails实现桌面番茄钟应用

0.项目背景 最近在优化自己的工作流&#xff0c;在工作方法上开始使用番茄钟来实现时间控制&#xff0c;一直觉得番茄钟的方式有点silly&#xff0c;直到自己用过之后才发现&#xff0c;番茄钟是真的好用&#xff0c;它不止是工作的方法&#xff0c;更是休息的艺术&#xff0c…

流程图还不会画?这个AI工具一键出图,还有上万模版库可选

我是娜姐 迪娜学姐 &#xff0c;一个SCI医学期刊编辑&#xff0c;探索用AI工具提效论文写作和发表。 流程图的绘制在工作中很常见。大家在写论文、课题申请等经常会用到需要绘制技术路线、工作流程、病人筛选流程等。 有没有AI工具能够根据我们的描述文字&#xff0c;一键生成流…

aspeed2600 openbmc对mctp over pcie的支持分析(支持mctp over pcie)

遍搜全网&#xff0c;竟然关于openbmc mctp over pcie的支持说明情况了无文章&#xff0c;这不是一个艰难的问题&#xff0c;服务器BMC也不是一个超级精尖的产品&#xff0c;想当年分享stm32资料&#xff0c;都是满天飞。可能服务器市场大家对于文章上的分享并无兴趣。 此篇文…

网络安全总结②

上一篇&#xff1a;网络安全总结① 下一篇&#xff1a; 传统防火墙 传统防火墙 技术&#xff1a;访问控制、代理技术、会话机制 工作层次&#xff1a;应用层一下 防御模式&#xff1a;通过防御设备划分边界&#xff0c;基于IP/端口和特征进行判断&#xff1b;以隔离为基础&am…

【Spring Boot 3】自定义拦截器

【Spring Boot 3】自定义拦截器 背景介绍开发环境开发步骤及源码工程目录结构总结背景 软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历中,每次学习新技术总是要花费或多或…