排序(1)——直接插入排序、希尔排序

 

目录

一、直接插入排序

1.简介

 2.思路与代码

3.复杂度与稳定性分析

(1)时间复杂度

(2)空间复杂度

(3)稳定性

二、希尔排序

1.简介

2.思路与代码

(1)分组排序

(2)多组并排

3.复杂度与稳定性分析

 (1)时间复杂度

(2)空间复杂度

(3)稳定性


         为了追求生产生活的便捷与秩序,我们常常需要对一系列的对象进行排序处理,排序在我们生活中无处不在。在C语言中面对众多的数据,众多优秀的程序员们发明了许多不同思想,不同方法的排序算法。我们今天所要接触到的就是其中之二:直接插入排序与希尔排序。

        注:本文以升序为例

一、直接插入排序

1.简介

        顾名思义,直接插入排序就是将数据直接插入到顺序之中。详细而言就是面对一个数组,假定某一位置之前的元素已经有序,需要将该元素也排入顺序之中,那么就需要这个元素与之前的顺序序列一一比较,找到其合适的位置插入进去。

 2.思路与代码

        在写排序算法的时候,最重要的是要明确单趟排序的思路,因为整体排序实际就是很多个单趟排序的不断重复。所以只要我们写出了单趟排序,把它丢进循环里,调整一下每一次排序的变量即可。

        对于一个数组a,它具有n个元素。对于任一元素下标end,假设[0,end]已经有序,我们所要完成的就是将下标为end+1(即有序序列后一个)的元素插入到有序序列对应的位置,使得有序范围变为[0,end+1]。对此我们需要将所需要插入的元素与有序序列从后往前一一比较,如若小于所比较的元素则证明应该插入在它的左边部分,那么就继续迭代与下一个位置比较;否则直接插入右边位置即可。

    //单趟排序	

    //[0,end]已经有序,将end+1插入有序数组内
	int end
	int	tmp = a[end + 1];
	while (end >= 0)
	{
		if (tmp < a[end])
		{
			a[end + 1] = a[end];
			end--;
		}
		else
		{
			break;
		}
	}
	a[end + 1] = tmp;

        那么对于整体而言,我们只需要采用循环的方式,将end从0开始,排到end成为最后一个元素为止。初值end=0,[0,0]单个元素有序;排完end=end-2这次循环后,end自增为n-1,说明[0,n-1]有序,即所有元素已经有序,就可以停止了。

void InsertSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		//[0,end]已经有序,将end+1插入有序数组内
		int end = i;
		int	tmp = a[end + 1];
		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}
}

3.复杂度与稳定性分析

(1)时间复杂度

        插入排序是从一个到多个的顺序扩展,其时间复杂度的主要贡献者是插入过程中寻找位置的过程。

        最差情况就是数组逆序的情况,此时每一次插入都需要完整地遍历之前的有序序列,所以此时的时间复杂度就是标准的等差数列求和,所以是O(n^2)

        最佳情况下,就是数组顺序的情况下,此时无需任何交换,对每个元素只需比较一次就确定了位置,所以相当于遍历了一遍数组,时间复杂度是O(n)

(2)空间复杂度

        插入排序并未用到多余额外的空间,所以空间复杂度是O(1)

(3)稳定性

        插入排序是稳定的。

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

        插入排序在排序过程中因为是依次取元素向前排的,只要保证在相等时end位置不向后挪动,即可确保相对位置不变,算法也就稳定了。

二、希尔排序

1.简介

        希尔排序是由DL.Shell于1959年提出,因而被命名为希尔排序。希尔排序可以看作是插入排序的升级版,它对插入排序取长补短。因为插入排序处理有序序列效率很高,而处理逆序序列效率很低,所以希尔采取预排序的方法,将数组用较低的代价排列成为接近有序的状态,再使用插入排序完成最后的排序,这样的方法使得排序的效率相较于插入排序有了巨大的飞跃。

2.思路与代码

        对于希尔排序,我们所需要重点解决的就是其预排序部分的实现,希尔排序在预排序中要求以gap为间隔进行预排序,即每隔gap个位置的元素称为一组,将这些组互不相干地分别进行插入排序。由于一次交换的发生在距离为gap的两个元素之间,因而使得较大元素更快被换到后方,而较小元素也更快换到前方。

        对于一个无序数组啊a,假定本次预排序gap为3。

        首先进行分组,将下标为0+n*3的元素视为一个组,另外两组下标为1+n*3和2+n*3,即每一组是下标加减gap的关系。我们这里说视为是因为并未为它们新开空间进行存放,只是我们人为将其主观划为三组,实际上其还是存在原数组中。这样理解比较方便,只是需要写代码的时候清楚注意到就好。

        下一步将三组分别进行插入排序,保证三组分别有序。

        最后放到原数组,这里说放回也是便于理解,同样地我们是在原数组中主观意识划分下的处理,分别排序这一步就是在原数组中进行调整,并不存在“放回”这一步。

(1)分组排序

        这种方案比较符合我们的认知逻辑,因为gap是间隔的距离,所以我们可以知道需要排序的组数也就是gap组,每组对应的首元素也就是前gap个元素,所以我们可以循环gap次来对每一组分别进行插入排序。

        直接插入排序的代码无需解释了,只是有一点改动。因为现在处理的是间隔为gap的一组数据,所以原先插入排序的调整(如end等)应该每次调整gap。

        除此之外,还需要补充的一点是gap的选取。当gap选的过小时,与直接插入排序区别不大开销会变大;如果选的过大则难以很好的实现“重沉轻浮”的目标。所以动态的gap才是好gap,gap先很大,尽快区分出数据,然后逐渐变小,进行细化。gap = gap / 3 + 1是业界普遍认定的比较好的选择,具体原因等我学会再告诉你们(略略略)。无论gap如何选取,我们都应该知道gap最后一步应该是1,这样相当于最后一步的直接插入排序,完成后才能宣告结束,所以gap的递推关系要满足使得gap递减,并且可以到达1。

void ShellSort(int* a, int n)
{
	//预排序

	//int gap = 3;
	int gap = n;
	//gap>1时是预排序
	//gap==1时是直接插入排序
	while (gap > 1)
	{
		//gap在循环中应该递减为1
		//gap = gap / 2;  //gap/2最后都可以到达1
		gap = gap / 3 + 1; //对gap除以3,商可能是任意整数并且一定比被除数小,即gap是递减的
		//对于gap/3循环,gap是有可能不经过1而直接到达0的,所以需要加1确保gap一定可以到达1
		for (int j = 0; j < gap; j++)
		{
			for (int i = j; i < n - gap; i += gap)//i作为坐标预处理数组元素下标
			{
				int end = i;//前end个元素已经有序
				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)多组并排

        多组并排和分组排序采取的思路一致,只是代码写法上有所不同。分组排序代码呈现与我们理解希尔排序的方式相同,而多组并排的代码呈现则是将其中两层循环合并起来了。因为原来代码每个组分别遍历自己的组员,每个组员的处理方式也都是一样的,这实际上就是遍历了整个原数组,所以多组并排就是遍历一遍原数组,遍历到不同的组员采取一样的处理方式。

void ShellSort2(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int cmp = a[i + gap];
			while (end >= 0)
			{
				if (cmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
				a[end + gap] = cmp;
			}
		}
	}
}

3.复杂度与稳定性分析

 (1)时间复杂度

        希尔排序的时间复杂度很明显不是我能算出来的(哭丧脸)。

        希尔排序最佳情况是数组顺序,时间复杂度为O(n)

        通过万能的网友大佬们,我得知希尔排序的一般情况下时间复杂度O(n^{1.3}) ~ O(n^{1.5})之间,还是很厉害的。

(2)空间复杂度

        希尔排序没有多用额外空间,空间复杂度为O(1)

(3)稳定性

        希尔排序是不稳定的。

        直接插入排序一样比较挪动只发生在相邻位置,可以控制使其稳定。但希尔排序存在大量的组内的大跨度交换,很容易调换相对顺序,所以是不稳定的。

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

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

相关文章

python222网站实战(SpringBoot+SpringSecurity+MybatisPlus+thymeleaf+layui)-帖子管理实现

锋哥原创的SpringbootLayui python222网站实战&#xff1a; python222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火爆连载更新中... )_哔哩哔哩_bilibilipython222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火…

Go的基准测试

基准测试&#xff08;Benchmark&#xff09;是一项用于测量和评估软件性能指标的方法&#xff0c;主要用于评估你写的代码的性能。 基准测试的代码文件必须以_test.go结尾基准测试的函数必须以Benchmark开头&#xff0c;必须是可导出的基准测试函数必须接受一个指向Benchmark类…

Blender教程-初始用户界面-01

开始第一天的Blender学习、也是业余学习。希望记录下这一份学习的过程、并且分享给大家。今天带大家认识Blender这一款软件&#xff0c;先说说我为什么选择了Blender&#xff0c;我在软件市场找了好久&#xff0c;市场上其他雷同软件都是要么收费要么不好用&#xff0c;最终决定…

文件包含漏洞长度截断

长度截断 文件漏洞的利用方式什么是长度截断通过实操理解00截断对版本要求更高一点&#xff0c;而长度截断则是利用了windows的系统漏洞&#xff0c;就是windows文件名&#xff08;就是文件名后缀之后&#xff09;之后如果有空格&#xff0c;或者是点都会被忽略掉&#xff0c;也…

安科瑞宿舍安全用电监测:科技保障,安全无忧

在当今社会&#xff0c;电力已成为我们日常生活中不可或缺的一部分。然而&#xff0c;不正确的用电方式或管理不善可能会引发火灾等安全事故&#xff0c;给学生带来生命财产威胁。为了解决这一问题&#xff0c;安科瑞宿舍安全用电监测系统应运而生&#xff0c;为学生的用电安全…

day05-盒子模型

01-选择器 结构伪类选择器 基本使用 作用&#xff1a;根据元素的结构关系查找元素。 li:first-child {background-color: green; } :nth-child(公式) 提示&#xff1a;公式中的n取值从 0 开始。 伪元素选择器 作用&#xff1a;创建虚拟元素&#xff08;伪元素&#xff09;…

JavaWeb01--Tomcat

1、JavaWeb概述 Web开发是基于请求和响应的&#xff1a; 请求&#xff1a;浏览器&#xff08;客户端&#xff09;向服务器发送信息 响应&#xff1a;服务器向浏览器回送信息 请求和响应是成对出现的。 Web资源分类 所谓Web资源即放在Internet网上供外界访问的文件或程序&#x…

基于springboot药房管理系统源码和论文

伴随着全球信息化发展&#xff0c;行行业业都与计算机技术相衔接&#xff0c;计算机技术普遍运用于药房管理行业。实施计算机系统来管理可以降低逍遥大药房管理成本&#xff0c;使整个逍遥大药房行业的发展有显著提升。 本论文主要面向逍遥大药房管理中出现的一些常见问题&…

Gin 应用多实例部署session问题、session参数与刷新

文章目录 一、Gin Session 存储的实现方案二、memstore&#xff1a;基于内存的实现2.1 基本使用2.2 关键参数 三、使用redis&#xff1a;多实例部署3.1 使用redis优势3.2 基本使用 四、信息安全的三个核心概念五、Gin Session 参数5.1 参数介绍 六、Session 自动刷新 一、Gin S…

jenkins+gitlab实现iOS自动打包的坎坷之路(本文包含CI\CD过程中的一些坑点以及一些理解及建议)

本文须知&#xff1a;本文成功案例是配置jekins所在服务器配置打包环境&#xff0c;并非在jenkins中配置打包环境。关于为何不采用在jenkins中配置打包环境将会在文中具体讲解。最后因为是基于jekins所在服务器配置的打包环境&#xff0c;按照本文所诉&#xff0c;实现ios自动打…

Java后端开发:学籍系统核心逻辑

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

SQL语句创建一个简单的银行数据库

目录 一、银行业务E-R图 二、数据库模型图 转换关系模型后&#xff1a; 三、创建数据库 3.1 创建银行业务数据库 四、创建表 4.1 创建客户信息表 4.2 创建银行卡信息表 4.3 创建交易信息表 4.4 创建存款类型表 结果如下&#xff1a; ​编辑 五、插入适量数据 5.1…

【Python】字符串

&#x1f6a9; WRITE IN FRONT &#x1f6a9; &#x1f50e; 介绍&#xff1a;"謓泽"正在路上朝着"攻城狮"方向"前进四" &#x1f50e;&#x1f3c5; 荣誉&#xff1a;2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2222年获评…

android开发者模式@adb无线调试

文章目录 adb调试功能介绍有线调试无线调试 配置无线adb调试手机端开发者选项配置电脑端配置步骤初次使用进行配对链接设备小结 检查链接是否成功 技巧快速打开无线调试 refs adb调试 功能介绍 ADB&#xff08;Android Debug Bridge&#xff09;是一种强大的命令行工具&#…

Linux初始相关配置

前言 在学完了Linux的相关基础命令后&#xff0c;在正式使用Linux系统之前&#xff0c;我觉得配置一些东西是很有意义的。 文章目录 前言1.权限配置&#xff0c;普通用户无法sudo提权2.vim配置3.vim其他操作4.动静态库5.gcc/g6.程序翻译的过程7.make/makefile8.cmake/CMakeLis…

docker拉取镜像时指定其OS及CPU指令集类型

前言 之前在香橙派5上安装的时候碰到过一次指定镜像的OS及cpu指令集类型的问题&#xff0c;但是当时没有记录&#xff0c;现在用到 了又想不起来&#xff0c;干脆就自己记录一下。预防后面忘掉。docker报错截图 上次时在arm的cpu中运行x86镜像&#xff0c;这次时在x86中运行arm…

仰暮计划|“星星之火可以燎原,平凡人的一生同样值得称赞

传递助老之情&#xff0c;践行为老初心。为学习和发扬助老为老精神&#xff0c;我参与了康乐忆享实践队开展的以“仰暮计划”为主题的实践活动&#xff0c;在实践过程中了解老人的人生经历&#xff0c;传播尊老爱老思想。我与老人谭爷爷在谈论家常时&#xff0c;他拿出年轻时的…

Blender教程-物体的移动、旋转与缩放-04

一、新建一个立方体 ShiftA新建一个立方体用来演示。 二、物体的移动 xyz轴移动 点击下图图左侧的移动选项后&#xff0c;选中要移动的物体&#xff0c;会出现三个箭头的方向&#xff0c;这分别代表沿着x、y、z轴移动。xyz平面移动 这个小正方体代表沿着某一个面移动&#…

基于JAVA的陕西非物质文化遗产网站 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 设计目标2.2 研究内容2.3 研究方法与过程2.3.1 系统设计2.3.2 查阅文献2.3.3 网站分析2.3.4 网站设计2.3.5 网站实现2.3.6 系统测试与效果分析 三、系统展示四、核心代码4.1 查询民间文学4.2 查询传统音乐4.3 增改传统舞…

程序员的平均结婚年龄

关于程序员的平均结婚年龄&#xff0c;根据之前的信息&#xff1a; 一项对全球10000名在职程序员的调查数据显示&#xff0c;程序员第一次结婚的平均年龄是39.43周岁。而在中国的部分地区&#xff0c;如北京等地&#xff0c;程序员群体中普遍反映的结婚年龄是在30岁左右。 程序…
最新文章