数据结构-排序

news2025/1/12 3:00:56

本节目标:

1.排序的概念及其运用
2.常见排序算法的实现
3.排序算法复杂度及稳定性分析
 

1.排序的概念及其应用 

1.1排序的概念 

排序就是按照某个我们设定的关键字,或者关键词,递增或者递减,完成这样的操作就是排序。

1.2排序的应用 

排序在日常生活中很常见的,想前几天刚出的软科高校排名,就用到了排序的思想,关键词就是软科,按照递增的顺序排列。如下图所示:

 

以及我们上淘宝的时候,也会用到排序的思想,我们想买个手机,筛选条件,按照个人选择不同给出的答案不同,用的人注重品牌,有的人注重价格,有的女孩更注重像素,如下图所示: 

 

1.3常见的排序算法 

2.常见的排序算法实现 

2.1插入排序 

2.1.1基本思想 

其实插入排序的思想,几乎我们每个人都会,插入排序就像我们打的扑克,从未知的一副牌中,我们开始往手里揭牌,拿起第一张放在手中,再次拿起一张牌的时候,就要和手里的牌对比,如果比手里的牌大就往后排,如果比手里的牌小的话,就排在这张牌之前,以此类推。一副牌拿完之后,我们手里的牌也就按照递增的顺序排好了。 

2.1.2直接插入排序: 

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

当插入的数比前一个数字还大时:

当插入的数字比数组中所有的数都要小的时候: 

 

 动态图解如下:

  

插入排序

代码实现:

void InsertSort(int* a, int n)
{
	//遍历数组中所有元素
	for (int i = 1; i < n; i++)
	{
		//单趟排序
		int end = i - 1;
		int temp = a[i];//temp存储的是元素数值

		while (end >= 0)//一直比较到 数组第一个元素完成
		{
			if (temp < a[end])
			{
				a[end + 1] = a[end];
				--end;
			}
			else // 包含两种情况,一种temp比所有元素都大,另一种temp比所有元素都小
			{
				break;
			}
		}
		a[end + 1] = temp;
	}
}

 结果对比:

在主程序中,我们调用插入排序函数,并查看最终是否实现 有序排序。 

void TestInsertSort()
{
	int a[] = { 3,5,1,6,2,3,7,9,0,8 };
	PrintArray(a, sizeof(a) / sizeof(int));

	InsertSort(a, sizeof(a) / sizeof(int));

	PrintArray(a, sizeof(a) / sizeof(int));
}

int main()
{
	TestInsertSort();
	return 0;
}

 

 分析总结:

直接插入排序时间复杂度:最坏情况下,逆序有序,每个元素都要一个个比较,最终形成等差数列, 1 +2 +3 + ........N 时间复杂度为 O(N^2) ;最好的情况下,有序,1 + 1 + 1 + .......1时间复杂度为O(N)  总结来看 对于插入排序,元素集合越接近有序,算法时间效率越高。

2.1.3希尔排序(缩小增量排序):  

     希尔排序也是插入排序的一种,它又称缩小增量排序法,他其实是在插入排序上的一种优化,通过上面的分析,我们知道插入排序时间复杂度最好的情况,也就是接近有序的时候,他的时间复杂度为 O(N),最坏的情况下 无序的时候 为 O(N^2)。如果我们能先让数组接近有序之后,在对他进行排序,会大大减小算法时间复杂度。所以希尔大佬就研究出了希尔排序。

它的实现主要通过两步骤,第一步:预排序,让数组接近有序,第二步:插入排序。

分组预排:

  将无序的数组,按照间隙gap进行分组,将数组分成n/gap组,然后分组进行插入排序,因为有了gap所以时间复杂度会减小,如下图所示:

 

 分组直接插入排序:

 

gap为3排序实现结果: 

 当gap依次减小,数组慢慢接近有序,最后gap为1时候,数组已经近似有序,再插入排序依次,数组就会实现有序的同时,算法时间复杂度最低。

代码实现:

void ShellSort(int* a, int n)
{
	int gap = 3;
	for (int j = 0; j < gap; j++)
	{
		for (int i = j; i <n- gap; i += gap)
			{
				//单趟排序
				int end = i;
				int temp =a[i + gap];//temp存储的是元素数值 i + gap 要在数组之内 不可以越界

				while (end >= 0)//一直比较到 数组第一个元素完成
				{
					if (temp < a[end])
					{
						a[end + gap] = a[end];
						end = end - gap;
					}
					else // 包含两种情况,一种temp比所有元素都大,另一种temp比所有元素都小
					{
						break;
					}
				}
				a[end + gap] = temp;
			}
	}
	
}

 改进一下:

void ShellSort(int* a, int n)
{
	int gap = 3;

	for (int i = 0; i <n- gap; i ++)
	{
		//单趟排序
		int end = i;
		int temp =a[i + gap];//temp存储的是元素数值 i + gap 要在数组之内 不可以越界

		while (end >= 0)//一直比较到 数组第一个元素完成
		{
			if (temp < a[end])
			{
				a[end + gap] = a[end];
				end = end - gap;
			}
			else // 包含两种情况,一种temp比所有元素都大,另一种temp比所有元素都小
			{
				break;
			}
		}
		a[end + gap] = temp;
	}
}

 我们发现gap越大虽然,跑的很快但是不接近有序,gap越小跑的慢,但是接近有序,所以需要设计合适的gap,减少复杂度的同时,保证最后一次排序 gap为1,通常 我们设计的gap,为n / 2,或者 n/3 - 1。

具体代码如下:

void ShellSort(int* a, int n)
{
	int gap =  n ;
	while (gap > 1)
	{
		gap /= 2;
		//gap = gap/3 - 1;
		for (int i = 0; i <n- gap; i ++)
		{
			//单趟排序
			int end = i;
			int temp =a[i + gap];//temp存储的是元素数值 i + gap 要在数组之内 不可以越界

			while (end >= 0)//一直比较到 数组第一个元素完成
			{
				if (temp < a[end])
				{
					a[end + gap] = a[end];
					end = end - gap;
				}
				else // 包含两种情况,一种temp比所有元素都大,另一种temp比所有元素都小
				{
					break;
				}
			}
			a[end + gap] = temp;
		}

	}
}

 结果:

 

分析总结: 

 希尔排序的时间复杂度分析:

 

 对于外层:是我们熟悉的二分或者三分 复杂度 最后为logN

 对于内部两层,当gap很大时候,可以看成N,当gap很小时,经过多次预排序,接近有序 复杂度也是N,我们在以gap/3分析中间的变化:图解分析如下:

所以 我们可以说 希尔排序时间复杂度近似 N*logN 但是每次预排都会有增益,他分组之后复杂度应该近似下图:

所以,虽然希尔的时间复杂度近似在N*logN这个等级,但是要比其大一点,查阅相关资料,对于希尔时间复杂度,通常是这么说的

 

2.2选择排序 

2.2.1基本思想:

 每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。

2.2.2直接选择排序

在元素集合array[i]--array[n-1]中选择关键码最大(小)的数据元素
若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换在剩余的array[i]--array[n-2](array[i+1]--array[n-1])集合中,重复上述步骤,直到集合剩余1个元素

 直接选择排序单趟寻找到最大的或最小的,实现升序的话就与最左边的交换,降序就与最右边交换,我们可以用代码实现左右共同查找的方式。如下所示:

void SelectSort(int* a, int n)
{
	//初试状态
	int left = 0;
	int right = n - 1;
	while (left < right)
	{
		//取最小值,最大值下标为最左边的 位置
		int min = left, max = left;
		for (int i = left + 1; i < right; i++)
		{
			if (a[i] < a[min])
			{
				min = i;
			}
	        else if (a[i] > a[max])
			{
				max = i;
			}

		}
		swap(&a[left], &a[min]);
		if (left == max) //防止出现 left位置上放的是最大值
		{
			max = min;
		}
		swap(&a[right], &a[max]);
		left++;
		right--;
	}
}

结果分析:

选择排序的时间复杂度,还是比较low的不管怎么选,都是O(N^2) 。

2.2.3堆排序

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

代码如下:

//向下调整
void AjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1; //孩子和父亲的关系

	while (child < n) // 遍历条件
	{
		if (child + 1 < n && a[child + 1] > a[child]) //左右孩子 选择最大的那个,同时兼顾 右孩子也要小于边界
		{
			++child;
		}
		if (a[child] > a[parent]) // 建大堆
		{
			swap(&a[child], &a[parent]);

			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}

	}

}
void HeapSort(int* a, int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; i--) // 从最后一个节点的父亲 开始向下调整 保证父亲比孩子大
	{
		AjustDown(a, n, i);
	}

	int end = n - 1;
	while (end > 0)
	{
	 swap(&a[end], &a[0]); // 交换 根节点 和最后一个位置的数值
	 AjustDown(a, end, 0);
	 --end;
	}
}

总结:

堆排的时间复杂度 ,我们在堆排序那个章节推过,这里我直接说结论:o(N * logN)。
 

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

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

相关文章

打开组策略提示:无法为文件xxx找到适当的资源文件,错误=2的解决方法

最近把自己的电脑升级成win11了&#xff0c;出现了一些bug&#xff0c;不见得是win11系统的问题&#xff0c;也可能是某个过程出现了问题&#xff0c;出现了问题咱就解决&#xff0c;参考了几个文档和视频&#xff0c;最终解决了&#xff0c;记录一下。 打开本地策略出现问题如…

7.Java中的String类、常用类及包装类

Java中的String类、常用类及包装类 一、String类 1、String类定义 String 类代表字符串。Java 程序中的所有字符串字面值&#xff08;如 “abc” &#xff09;都作为此类的实例实现。字符串是常量&#xff1b;它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为…

2023爱分析·中国城市轨交智能运维市场厂商评估报告:逸迅科技

报告编委 张扬 爱分析联合创始人&首席分析师 王鹏 爱分析分析师 目录 1. 研究背景 2. 市场综述 3. 市场分析 4. 厂商评估&#xff1a;逸迅科技 5. 最佳实践案例 1. 研究背景 轨道交通是我国国民经济的命脉和交通运输的骨干网络&#xff0c;不仅承担了绝大…

RocketMQ 消息发送源码解读

可靠同步发送、可靠异步发送、单向发送、批量消息发送。 RocketMQ 消息发送需要考虑以下3个问题。 1&#xff09;消息队列如何进行负载&#xff1f; 2&#xff09;消息发送如何实现高可用&#xff1f; 3&#xff09;批量消息发送如何实现一致性&#xff1f; org.apache.rocketm…

基于SpringBoot+微信小程序的失物招领小程序

基于SpringBoot微信小程序的失物招领小程序 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目…

通达信欧奈尔RPS指标公式编写和设置方法(完全版)

通达信欧奈尔RPS指标公式的编写和设置较为复杂&#xff0c;对于初学者来说可能具有一定挑战性。在编写口袋支点公式时&#xff0c;需要使用RPS指标公式作为基础条件&#xff0c;因此有必要先了解其编写和设置方法。 一、上市一年以上选股 首先选出上市一年以上的股票&#xff…

python实现折线图和条形图

画图的部分函数 pandas 读取数据 相关包&#xff1a;import pandas as pd 函数&#xff1a;dfpd.read_excel(“文件名”) #读取excel文件 df.head(n)#查看前n行 df.tail(n)#查看后n行 df.shape #查看行数和列数 df.columns # 查看列索引 df.index #查看行索引 df.info() #查看…

怎样恢复删除的视频?视频恢复,4个方法!

案例&#xff1a;怎样恢复删除的视频 【谁懂啊&#xff01;电脑里视频太多了太占内存&#xff0c;本想删除一些不太重要的&#xff0c;但却删错了&#xff01;有朋友知道怎样恢复删除的视频吗&#xff1f;】 在数字化时代&#xff0c;我们经常使用电脑来存储和管理各种视频文…

股票量比实时筛选报警

一.什么是股票的量比 量比是短线投资一个参考指标&#xff0c;是衡量相对成交量的一个数值和指标&#xff0c;用于反映股票交易相对于以往的活跃程度&#xff1b;是指在股市开盘以后&#xff0c;平均每一分钟的成交量与过去五个交易日平均每分钟交易量的比。反映股票相对最近5…

自动驾驶定位模块的作用是什么?为什么会有多种坐标系?

无人车,要实现自动驾驶,首先要知道自己的的位置。更准确的说法是:相对某个坐标系,确定车辆的位置和姿态。 这个坐标系可以是局部的: 也可以是全局坐标系: 这是更大维度上的坐标系。 坐标系确定之后,相对坐标原点和坐标轴,车上坐标系(本地坐标系),平移得到位置(x…

SQL语句要点一文速览

以下内容参考《SQL必知必会&#xff08;第4版&#xff09;》 了解 SQL 数据库&#xff08;database&#xff09;&#xff1a;保存有组织的数据的容器&#xff08;通常是一个文件或一组文件&#xff09;。最简单的办法是将数据库想象为一个文件柜。这个文件柜是一个存放数据的…

【数据结构】算法的时间复杂度和空间复杂度(下)(附leetcode练习题)

☃️个人主页&#xff1a;fighting小泽 &#x1f338;作者简介&#xff1a;目前正在学习C语言和数据结构 &#x1f33c;博客专栏&#xff1a;数据结构 &#x1f3f5;️欢迎关注&#xff1a;评论&#x1f44a;&#x1f3fb;点赞&#x1f44d;&#x1f3fb;留言&#x1f4aa;&…

【Linux】system V 共享内存

文章目录system V1. 共享内存原理第一阶段原理第二阶段原理2. 直接写代码--编写代码进行原理介绍shmget函数ftok函数key值用法1. 创建key值2. 创建共享内存 获取共享内存3. 将自己和共享内存关联起来4. 将自己和共享内存取消关联5. 删除共享内存用指令删除调用系统调用完整代码…

数据库管理-第六十六期 SQL Domain(20230413)

数据库管理 2023-04-13第六十六期 SQL Domain1 基本介绍2 Domain的表达式和条件3 语法4 语义5 示例总结第六十六期 SQL Domain 上一期一笔带过了部分Oracle 23c的新特性&#xff0c;这一期重点讲一下SQL Domain新特性。 【https://docs.oracle.com/en/database/oracle/oracle-…

【提升效率神器】Python简单批量生成PDF文档(详细做法)

文章目录前言一、准备二、基本使用三、批量生成PDF总结前言 日常办公中&#xff0c;经常会使用PDF文档&#xff0c;难免需要对PDF文档进行编辑&#xff0c;有时候PDF文档中的大部分内容都是一样的&#xff0c;只是发送对象不同。 这种模板套用的场景下&#xff0c;使用Python…

BI 知识大全,值得收藏的干货

01、什么是商业智能BI&#xff1f; 商业智能BI可以实现业务流程和业务数据的规范化、流程化、标准化&#xff0c;打通ERP、OA、CRM等不同业务信息系统&#xff0c;整合归纳企业数据&#xff0c;利用数据可视化满足企业不同人群对数据查询、分析和探索的需求&#xff0c;从而为…

OpenCV实例(三)答题卡识别

OpenCV实例&#xff08;三&#xff09;答题卡识别1.答题卡识别概述2.单道题目的识别2.1基本流程及原理2.2代码实例&#xff1a;作者&#xff1a;Xiou 1.答题卡识别概述 随着信息化的发展&#xff0c;计算机阅卷已经成为一种常规操作。在大型考试中&#xff0c;客观题基本不再…

重整网站。。。。。。。。。

重整网站 写好回复的人 “ xxxxxxxx”通知栏&#xff0c;并且快速跳转到需要的页面。个人页面&#xff0c;记录自己发送的消息与回复的信息。以css 上传的图片防止被拉伸拉坏。 下拉的选择下拉的分页的好处。 评论功能的那一栏中的一个小的评论&#xff0c;如果手机端的话&a…

RabbitMQ 保证消息不丢失的几种手段

文章目录1.RabbitMQ消息丢失的三种情况2.RabbitMQ消息丢失解决方案2.1 针对生产者2.1.1 方案1 &#xff1a;开启RabbitMQ事务2.1.2 方案2&#xff1a;使用confirm机制2.2 Exchange路由到队列失败2.3 RabbitMq自身问题导致的消息丢失问题解决方案2.3.1 消息持久化2.3.2 设置集群…

无废话硬核分享:Linux 基础知识点总结很详细,全的很,吐血奉献

Linux 的学习对于一个程序员的重要性是不言而喻的。前端开发相比后端开发&#xff0c;接触 Linux 机会相对较少&#xff0c;因此往往容易忽视它。但是学好它却是程序员必备修养之一。 Linux 基础 操作系统 操作系统Operating System简称OS&#xff0c;是软件的一部分&#x…