c语言-希尔排序

news2024/11/18 8:25:21

目录

一、插入排序

1、插入排序的概念

2、插入排序的逻辑实现

 3、插入排序的实现

二、希尔排序

1、希尔排序概念

2、希尔排序逻辑实现

3、间隔值(gap)对排序的影响

 4、希尔排序的实现

三、插入排序与希尔排序性能对比测试

结语:


前言:

        希尔排序的核心思想就是插入排序,他是在插入排序的基础上进行优化得来的,因此要想明白希尔排序的逻辑首先要认识插入排序。

一、插入排序

1、插入排序的概念

        插入排序又名直接插入排序,其思想是把要插入的数值,插入到一个有序的序列中,从而得到一个新的有序序列。现实中就跟摸牌一样,每次摸一张牌都会把这张牌插入到手中牌堆的合适位置,从而让手里的牌变得有序。

2、插入排序的逻辑实现

        排序的目的是让数组中的元素变得有序,比如冒泡排序的逻辑是从前往后遍历、相邻元素进行比较从而达到排序的功能。而插入排序的逻辑是从数组的最后一个元素往前遍历数组,举例:现如今要插入一个元素2,如果遇到大于2的元素则该元素往后“挪动”一位,直到遇到小于2的元素将2插入到该元素的后一位。

        另一种情况就是当数组内的元素都比要插入的元素要大:

 3、插入排序的实现

        在明白插入排序的逻辑后,以下用代码的形式将插入排序实现,并且测试最终结果。



#include<stdio.h>

void InsertSort(int* a, int n)//插入排序
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;//表示当前数组的最后一个元素的下标
		int temp = a[i + 1];//一般而言,把end后一个元素看作是要插入进行排序的元素
		//因此为了防止该元素被end的元素所覆盖,因此要先保存起来

		while (end >= 0)//end小于0说明数组内元素都大于该插入的元素
		{
			if (a[end] > temp)//大于插入元素的情况
			{
				a[end + 1] = a[end];//“挪动”
				end--;//遍历end
			}
			else//小于或等于插入元素的情况
				break;//直接跳出
		}
		a[end + 1] = temp;//不论大于还是小于插入元素,都把要插入的元素给到end位置的后一位
	}
}

void PrintArr(int* a, int n)//打印数组
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

void Test_InsertSort()//插入排序测试函数
{
	int arr[] = { 66,8,6,1,2,4,3,9 };
	int sz = sizeof(arr) / sizeof(int);
	PrintArr(arr, sz);
	InsertSort(arr, sz);
	PrintArr(arr, sz);

}

int main()
{
	Test_InsertSort();

	return 0;
}

        其实在对数组进行插入排序时,都是在数组内进行操作的,实际上不存在“从外边来了一个元素”要插入这个数组,而是从数组的第二个元素开始,把第二个元素看成是“要插入”的元素进行插入排序的。示意图如下:

         运行结果:

二、希尔排序

1、希尔排序概念

        希尔排序是在插入排序的基础上进行优化而来的,因为插入排序如果是对一个比较有顺序的数组进行排升序,那么其时间复杂度是O(N),但是如果插入排序要把一个降序的数组排成升序,那么他的时间复杂度就等于O(N^2),而希尔排序优势就是在面对这种情况的时间复杂度依然可以做到O(N)。

        希尔排序法又称缩小增量法,他的具体步骤可以分成两步:1、预排序,就是把要排序的数组按照某个间隔值分成若干个小数组。2、然后对这若干个数组进行插入排序。其中,让间隔值不断的缩小,直到间隔值为1时,希尔排序=插入排序,只不过在这个过程中希尔排序已经让数组变得有序了,所以哪怕最后希尔排序的效率=插入排序效率,总体而言希尔排序的效率是比插入排序要高的。详细如下文。

2、希尔排序逻辑实现

        由于插入排序面对降序转升序的情况不好处理,因此介绍希尔排序的时候用一个降序的数组作为例子,现在要对一个降序的数组进行排升序,逻辑图如下:

        待到红、蓝、绿三个数组全部都排好之后,数组内顺序如下:

        可以发现此时数组里的元素并不是一个升序状态,但是比处理前更接近升序了,这时候再对其进行插入排序则可以完成对该数组的升序排序,而且时间消耗也不大。

        代码实现:

void ShellSort(int* a, int n)//希尔排序
{
	int gap = 3;//假设gap=3
		for (int j = 0; j < gap; j++)//控制小数组
		{
			for (int i = j; i < n - gap; i += gap)//对小数组进行排序
			{
                //插入排序的逻辑
				int end = i;
				int temp = a[end + gap];//注意小数组之间的间隔是gap
				while (end >= 0)
				{
					if (a[end] > temp)
					{
						a[end + gap] = a[end];
						end -= gap;
					}
					else
						break;
				}
				a[end + gap] = temp;
			}
		}
}

        这里gap=3只能让该数组变得接近升序而不能排成升序,上文还提到了一个问题就是当gap=1时,希尔排序=插入排序,因此如果可以控制gap的值的变化,最后让gap=1就能完成对数组的升序排序了。那为什么gap=1时,希尔排序=插入排序呢,gap对排序的影响详细如下文。

3、间隔值(gap)对排序的影响

         为什么间隔值gap=1的时候希尔排序=插入排序,从上文假设当gap=3,发现小数组中的元素与元素之间相隔了2个元素,那么gap=1时,元素与元素之间相隔0个元素,此时再对这些”小数组“进行排序实则就是对整个数组进行插入排序。

        从上图可以得出,当gap=1时就是等价于插入排序的,那么gap=3、=5时会对数组里元素的顺序有什么影响吗,如下图所示:

        可以发现当gap=1时,排出来的就是一个升序数组。

        当gap=3时,排出来的结果接近升序数组。

        当gap=5时,排出来的结果反而没那么接近升序了。

结论:当gap的值越大,则越不接近升序,当gap的值越小则越接近升序。但是gap值越大有一个好处是可以把数组中数值较大的元素挪到数组的后面,较小的值放到前面。因为间隔越大,则一次移动的步长就大。

        但是gap一开始不能设置成1,不然希尔排序就没有意义了,希尔排序的优势是让gap>1的时候快速的把数组调成接近有序的,然后再用插入排序进行排序。

        因此gap的值的变化是一个从大到1的过程,可以用gap=gap/3+1来表示(gap初始值设置为该数组的元素个数),并且作为循环内部的表达式,这样一来gap的值就会不断的缩小至1,其中n表示数组的元素个数,+1的目的是保证让gap最小值为1,若不+1则会出现gap=0的情况,就会发生死循环了。

 4、希尔排序的实现

        在了解了希尔排序的逻辑以及gap的值对排序结果的影响后,用代码将其实现:



#include<stdio.h>

void ShellSort(int* a, int n)//希尔排序
{
	int gap = n;//gap初始化为n,n表示数组内元素个数
	while (gap > 1)
	{
		gap = gap / 3 + 1;//第一次循环先带着较大的gap去排序,后续gap的值慢慢变小,最后至1
		for (int j = 0; j < gap; j++)//控制小数组
		{
			for (int i = j; i < n - gap; i+=gap)//将小数组内的元素进行排序
			{
				//插入排序思想
				int end = i;
				int temp = a[end + gap];//这里小数组内的元素间隔是gap,而不是1
				while (end >= 0)
				{
					if (a[end] > temp)
					{
						a[end + gap] = a[end];
						end -= gap;
					}
					else
						break;
				}
				a[end + gap] = temp;
			}
		}
		
	}
}

void PrintArr(int* a, int n)//打印数组
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

void Test_ShellSort()//希尔排序测试函数
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(int);
	PrintArr(arr, sz);
	ShellSort(arr, sz);
	PrintArr(arr, sz);

}

int main()
{
	Test_ShellSort();

	return 0;
}

        运行结果:

三、插入排序与希尔排序性能对比测试

        从代码的结构来看,会感觉到希尔排序的写法很复杂,而且逻辑也不简单,那么看起来如此复杂的希尔排序的性能一定比插入排序的性能要好吗,以下用一个测试代码进行对他们的性能测试:



#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<time.h>

void ShellSort(int* a, int n)//希尔排序
{
	int gap = n;//gap初始化为n,n表示数组内元素个数
	while (gap > 1)
	{
		gap = gap / 3 + 1;//第一次循环先带着较大的gap去排序,后续gap的值慢慢变小,最后至1
		for (int j = 0; j < gap; j++)//控制小数组
		{
			for (int i = j; i < n - gap; i += gap)//将小数组内的元素进行排序
			{
				//插入排序思想
				int end = i;
				int temp = a[end + gap];//这里小数组内的元素间隔是gap,而不是1
				while (end >= 0)
				{
					if (a[end] > temp)
					{
						a[end + gap] = a[end];
						end -= gap;
					}
					else
						break;
				}
				a[end + gap] = temp;
			}
		}

	}
}

void InsertSort(int* a, int n)//插入排序
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;//表示当前数组的最后一个元素的下标
		int temp = a[i + 1];//一般而言,把end后一个元素看作是要插入进行排序的元素
		//因此为了防止该元素被end的元素所覆盖,因此要先保存起来

		while (end >= 0)//end小于0说明数组内元素都大于该插入的元素
		{
			if (a[end] > temp)//大于插入元素的情况
			{
				a[end + 1] = a[end];//“挪动”
				end--;//遍历end
			}
			else//小于或等于插入元素的情况
				break;//直接跳出
		}
		a[end + 1] = temp;//不论大于还是小于插入元素,都把要插入的元素给到end位置的后一位
	}
}


//对比测试
void Contrast_test()
{
	int n = 100000;
	srand(time(0));
	int* n1 = (int*)malloc(sizeof(int) * n);
	int* n2 = (int*)malloc(sizeof(int) * n);
	int* n3 = (int*)malloc(sizeof(int) * n);
	int* n4 = (int*)malloc(sizeof(int) * n);
	int* n5 = (int*)malloc(sizeof(int) * n);

	for (int i = 0; i < n; i++)//构建一个100000个元素的数组,并且存放随机值
	{
		n1[i] = rand() % 10000;
		n2[i] = n1[i];
		n3[i] = n1[i];
		n4[i] = n1[i];
		n5[i] = n1[i];
	}

	//clock函数返回的是系统启动到调用该函数的时间,单位是毫秒,并存到变量中
	int start1 = clock();
	InsertSort(n1, n);
	int end1 = clock();

	int start2 = clock();
	ShellSort(n2, n);
	int end2 = clock();


	printf("Test_InsertSort:%d\n", end1 - start1);//两个变量相减从而得到排序所消耗的时间
	printf("Test_ShellSort:%d\n", end2 - start2);
	

	free(n1);//释放空间
	free(n2);
	free(n3);
	free(n4);
	free(n5);
}

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

        运行结果:

        从结果可以观察到,插入排序的速度要比希尔排序的速度慢很多,也可以证明插入排序的消耗确实比希尔排序要大。 

结语:

        以上就是关于希尔排序的全部介绍以及实现,在掌握了希尔排序后会发现对插入排序的理解更加深刻了,其实各种不同的排序都有其存在的价值,也都有其适合的场景。最后希望本文可以给你带来更多的收获,如果本文对你起到了帮助,希望可以动动小指头帮忙点赞👍+关注😎+收藏👌!如果有遗漏或者有误的地方欢迎大家在评论区补充~!!谢谢大家!!

(~ ̄▽ ̄)~

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

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

相关文章

官宣的2023年汉字小达人市级比赛的安排和重点解读

昨天&#xff0c;汉字小达人在官微发布了关于2023年汉字小达人市级比赛的安排&#xff0c;基本上把大家都关心的事项都说了。 但是有一个很关键的点&#xff0c;今年的提法和往年不一样&#xff0c;多了一句话&#xff1a;2023年《中文自修•聪明小豆丁》七八月合刊上的模拟题仅…

数据结构 / day06 作业

1.下面的代码打印在屏幕上的值是多少? /下面的代码打印在屏幕上的值是多少?#include "stdio.h"int compute_data(int arr[], unsigned int len) {long long int result 0;if(result len)return arr[0];resultcompute_data(arr,--len);printf("len%d, res…

AB|如何正确从罗克韦尔官网下载资料?

哈喽呀&#xff0c;大家好&#xff0c;我是雷工&#xff01; 作为工控行业的从业者&#xff0c;可能要和各个厂家的中控系统、PLC、触摸屏、变频器、等软硬件产品打交道。 虽然从业十余年&#xff0c;但也不可能接触使用过所有的工控产品。还有海量的产品是没有接触过的。 但很…

IntelliJ IDEA 中有什么让你相见恨晚的技巧

一、条件断点 循环中经常用到这个技巧&#xff0c;比如&#xff1a;遍历1个大List的过程中&#xff0c;想让断点停在某个特定值。 参考上图&#xff0c;在断点的位置&#xff0c;右击断点旁边的小红点&#xff0c;会出来一个界面&#xff0c;在Condition这里填入断点条件即可&…

【并发编程】ConcurrentHashMap底层结构和原理

&#x1f4eb;作者简介&#xff1a;小明Java问道之路&#xff0c;2022年度博客之星全国TOP3&#xff0c;专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化&#xff0c;文章内容兼具广度、深度、大厂技术方案&#xff0c;对待技术喜欢推理加验证&#xff0c;就职于…

Elasticsearch底层原理分析——新建、索引文档

es版本 8.1.0 重要概念回顾 Elasticsearch Node的角色 与下文流程相关的角色介绍&#xff1a; Node Roles配置主要功能说明masternode.roles: [ master ]有资格参与选举成为master节点&#xff0c;从而进行集群范围的管理工作&#xff0c;如创建或删除索引、跟踪哪些节点是…

Nacos2.x配置中心源码分析

概述 源码注释参考 git 仓库&#xff0c;对应流程图后续补充&#xff1b; 启动 nacos nacos 启动类&#xff1a; // com.alibaba.nacos.NacosSpringBootApplication(scanBasePackages "com.alibaba.nacos") ServletComponentScan EnableScheduling public class…

Django < 2.0.8 在 CommonMiddleware 中打开重定向的可能性 (CVE-2018-14574)

漏洞描述 如果django.middleware.common.CommonMiddleware和APPEND_SLASH设置都已启用&#xff0c;并且项目的 URL 模式接受任何以斜杠结尾的路径&#xff0c;则对该网站的恶意制作的 URL 的请求可能会导致重定向到另一个网站&#xff0c;从而启用网络钓鱼和其他攻击。 漏洞环…

带着GPT-4V(ision)上路,自动驾驶新探索

On the Road with GPT-4V(ision): Early Explorations of Visual-Language Model on Autonomous Driving GitHub | https://github.com/PJLab-ADG/GPT4V-AD-Exploration arXiv | https://arxiv.org/abs/2311.05332 自动驾驶技术的追求取决于对感知、决策和控制系统的复杂集成。…

第八节HarmonyOS @Component自定义组件的生命周期

在开始之前&#xff0c;我们先明确自定义组件和页面的关系&#xff1a; 1、自定义组件&#xff1a;Component装饰的UI单元&#xff0c;可以组合多个系统组件实现UI的复用。 2、页面&#xff1a;即应用的UI页面。可以由一个或者多个自定义组件组成&#xff0c;Entry装饰的自定…

消息队列进阶-1.消息队列的应用场景与选型

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码、Kafka原理&#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44…

Nacos源码本地搭建流程及目录结构解读

下载地址 https://github.com/alibaba/nacos 目录结构 本地单机启动 首先maven编译完成之后在console下面找到Nacos 这个就是主启动类 然后再vm中配置参数-Dnacos.standalonetrue表示单机启动 当控制台没有报错 访问 http://localhost:8848/nacos 控制台界面登录进来之后显…

LLM能力与应用全解析

一、简介 经过几年时间的发展&#xff0c;大语言模型&#xff08;LLM&#xff09;已经从新兴技术发展为主流技术。而以大模型为核心技术的产品将迎来全新迭代。大模型除了聊天机器人应用外&#xff0c;能否在其他领域产生应用价值&#xff1f;在回答这个问题前&#xff0c;需要…

澳大利亚访问学者子女入学政策-附实例体会

很多访问学者出国交流时&#xff0c;希望子女携签&#xff0c;一起到异国体验不同的生活方式&#xff0c;拓宽视野&#xff0c;增加认知。如果能免费入读当地的公立中小学&#xff0c;还可以获得自然习得英语的机会。那么澳大利亚的访问学者能否达到这一目的&#xff1f;需要准…

TiDB 7.x 源码编译之 TiDB Server 篇,及新特性详解

本文将介绍如何编译 TiDB Server 源码。以及阐释 TiDB Server 7.x 的部分新特性。 TiDB v7.5.0 LTS 计划于 2023 年 11 月正式 Release&#xff0c;目前代码虽未冻结&#xff0c;但已经可以看到 Alpha 版本的 Code 了&#xff0c;本文代码将以 v7.5.0-alpha 为基准。 TiDB Se…

【substance painter】如何制作一个生锈磨损的枪

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

智能优化算法应用:基于花授粉算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于花授粉算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于花授粉算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.花授粉算法4.实验参数设定5.算法结果6.参考文献7.…

C++ string类(二)

insert&#xff1a; erase&#xff1a; 常见用法&#xff1a; int main() {string s1("hello world");string s2("gm");s1.insert(5,"x");cout << s1 << endl;s1.insert(6,s1,0);cout << s1 << endl;s1.insert(0,&qu…

conda环境下 ERROR: CMake must be installed to build dlib问题解决

1 问题描述 pip install -r requirements.txt 在构建video_retalking项目过程中&#xff0c;使用命令安装依赖包时&#xff0c;出现如下错误&#xff1a; Building wheels for collected packages: face-alignment, dlib, ffmpy, futureBuilding wheel for face-alignment …

Unity中Shader的BRDF解析(三)

文章目录 前言一、BRDF中的镜面反射项二、分别解析每一个参数1、D、G函数&#xff1a;speclarTerm2、其他中间步骤3、光照颜色4、F函数&#xff08;菲涅尔函数&#xff09; &#xff1a;FresnelTermIBL在下篇文章中继续解析 三、最终代码.cginc文件:Shader文件&#xff1a; 前言…