【数据结构初阶】希尔排序

news2024/10/7 8:22:36

鼠鼠最近学习了希尔排序,做个笔记!

希尔排序也是插入排序的一种捏!本篇博客也是用排升序来举例捏!

希尔排序是基于直接插入排序的,是由大佬D.L.Shell提出的。

目录

1.希尔排序

1.1.预排序

1.2.直接插入排序

2.希尔排序的时间复杂度

3.希尔排序和直接插入排序的性能比较


1.希尔排序

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个 组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。如果不好懂没关系,继续看下面讲解:

鼠鼠上一篇博客介绍了直接插入排序应用时待排序的数组越接近有序,直接插入排序算法的时间效率越高。基于这个,D.L.Shell将希尔排序分为预排序和直接插入排序!

1.1.预排序

对待需排序的乱序数组,D.LShell不直接使用直接插入排序。他先搞一搞预排序。

预排序:

首先需排序的乱序数组分成gap组(若干组)。举个栗子吧:

让所有距离为gap的数分为一组,如图举例分为了gap=3组:蓝色组、红色组和绿色组。 

然后分别将蓝色组、红色组和绿色组的数据进行直接插入排序,这样每组数据排列都有序了,如图:

预排序其实大有作用,它能让待排序的乱序数组中大的数据尽量往后靠,让小的数据尽量往前靠,这样的话待排序的乱序数组就更接近有序。

那么我们来看看代码的推理过程:

1.我们先搞定蓝色组的直接插入排序的“单趟”:

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

 2.同样我们用循环控制好end就搞定了蓝色组的直接插入排序:

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

3.但是我们有gap组需要直接插入排序,那么我们再套一层循环循环gap次让不同组直接插入排序即可搞定预排序:

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

这种写法是让同一组直接插入排好再排下一组。

 鼠鼠下面再展示一种写法,其实与上面写法本质是一样的:

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

这种写法其实是多组并列插入排序 ,老爷们体会一下,鼠鼠很难解释捏!

预排序的代码我们暂且写到这里,我们可以看到:gap越大,大的数据越快往后靠,小的数据越快往前靠;但是需排序乱序数组整体越不接近有序。gap越小,则相反;当gap小到等于1时,就是直接插入排序,能让需排序乱序数组直接变成有序的。

1.2.直接插入排序

我们经过一次预排序是不是直接就让需排序乱序数组来直接插入排序呢?

其实不是的,因为经过一次预排序不能保证需排序乱序数组接近有序,只能保证比没有预排序之前更加有序,经过一次预排序就直接让需排序乱序数组来直接插入排序的话效率没有多大提升!

而且如果只进行一次预排序的话,gap就是一个定值,gap是定值是不合适的。如果gap确定是3,但需排序乱序数组数据个数有10000个的话,每组就有300多个数据要排,不合适!

其实主流玩法已尽解决了这些个问题,只要进行多组预排序就行,我们来看希尔排序的完整代码再分析:

//希尔排序排升序
void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int j = 0; j < n - gap; j ++)
		{
			int end = j;
			int tmp = a[end + gap];
			for (end; end >= 0; end -= gap)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

这样子我们将gap设置成变化的,gap会越来越小,当gap不等于1时是预排序。相当于第一轮预排序大概是3个数据为一组排;第二轮预排序大概是9个数据为一组排;第三轮预排序是27个数据为一组排……而且每一轮预排序过后就越有序。

我们再分析发现,gap一定会变成1,那就是让需排序乱序数组直接来一把直接插入排序,这把直接插入排序过后循环结束并且需排序乱序数组就变成有序的了,而且经过了多次预排序最后来一把直接插入排序时间效率会很高!

当然gap如何变化都没有规定,我们也可以写成gap=gap/2……反正要保证最后一次循环的时候gap要等于1,才能保证最后一把是让需排序乱序数组整体进行直接插入排序。

我们来试试希尔排序得不得行:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

//希尔排序排升序
void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int j = 0; j < n - gap; j ++)
		{
			int end = j;
			int tmp = a[end + gap];
			for (end; end >= 0; end -= gap)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

int main()
{
	int a[] = { 1,5,8,7,9,6,48,3,5,99,6,3,7,5 };
	PrintArray(a, sizeof(a) / sizeof(a[0]));
	ShellSort(a, sizeof(a) / sizeof(a[0]));
	PrintArray(a, sizeof(a) / sizeof(a[0]));
	return 0;
}

结果是没问题的!

2.希尔排序的时间复杂度

希尔排序有太多不确定性,所以时间复杂度不好计算,大多数人认为是O(N^1.3)。

3.希尔排序和直接插入排序的性能比较

也许老爷们会认为希尔排序弄那么多次预排序加一次直接插入排序,时间效率所不定还不如直接插入排序来的好。

其实不然,我们用一个程序比较比较就可以看出结果,这个程序用到一个C语言库里面的函数clock,clock函数的大致作用是获取从系统启动到调用这个clock函数之间的毫秒数。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<time.h>
#include<stdlib.h>

//希尔排序排升序
void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int j = 0; j < n - gap; j ++)
		{
			int end = j;
			int tmp = a[end + gap];
			for (end; end >= 0; end -= gap)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

//直接插入排序排升序
void InsertSort(int* a, int n)
{
	for (int j = 0; j < n - 1; j++)
	{
		int end = j;
		int tmp = a[end + 1];
		for (end; end >= 0; end--)
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}
}

int main()
{
	int n = 100000;
	int* a1 = (int*)malloc(sizeof(int) * n);
	int* a2 = (int*)malloc(sizeof(int) * n);
	srand((unsigned int)time(0));
	for (int i = 0; i < n; i++)
	{
		a1[i] = rand();
		a2[i] = a1[i];
	}

	int begin1 = clock();
	ShellSort(a1, n);
	int end1 = clock();

	int begin2 = clock();
	InsertSort(a2, n);
	int end2 = clock();

	printf("ShellSort:%d\n", end1 - begin1);
	printf("InsertSort:%d\n", end2 - begin1);
	return 0;
}

我们看到结果,排序10万个一模一样的随机数,希尔排序用22毫秒,而直接插入排序用4490毫秒! 

感谢阅读!

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

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

相关文章

jetson实操(二):jetson nano发送短信到指定用户

文章目录 一、准备工作二、代码实现 一、准备工作 腾讯云网址&#xff1a;点击 注&#xff1a;需先申请“短信签名”和“短信正文”&#xff0c;按照要求填写申请即可&#xff0c;腾讯云的审核效率还是很快的&#xff0c;一般在1-2个小时内就会有结果&#xff0c;链接&…

Linux环境创建普通用户,授权root权限。报错:usermod: group ‘sudo‘ does not exist

在Linux环境下&#xff0c;创建普通用户并授权root权限需要以下步骤&#xff1a; 1. 以root用户登录终端。 2. 执行以下命令创建一个新的用户&#xff0c;其中username为你想要创建的用户名&#xff0c;可根据实际情况自行更改。 adduser username 3. 设置该用户的密码&…

数据库原理与应用实验三 嵌套查询

实验目的和要求 加深和掌握对嵌套查询的理解和应用 实验环境 Windows10 SQLServer 实验内容与过程 图书&#xff08;书号&#xff0c;书名&#xff0c;价格&#xff0c;出版社&#xff09; 读者&#xff08;卡号&#xff0c;姓名&#xff0c;年龄&#xff0c;所属单位&a…

luci框架相关笔记

luci架构 LuCI 架构采用了MVC&#xff08;Model-View-Controller&#xff09;设计模式&#xff0c;各个目录的作用如下&#xff1a; model&#xff08;模型&#xff09;: 位于 /usr/lib/lua/luci/model 下&#xff0c;存放了与系统配置相关的模型脚本。这些脚本负责与底层系统…

有免费的通配符SSL证书吗?通配符证书的申请

首先要了解通配符SSL证书&#xff0c;需要先知晓我们常用的普通单域名SSL证书、多域名SSL证书与之的区别。 单域名SSL证书最容易理解&#xff0c;一张证书有且只能绑定与保护一个域名&#xff0c;例如www.123456.com 证书安装部署完成后则会激活对于该域名的https、即加密访问…

【Leetcode 42】 接雨水

基础思路&#xff1a; &#xff08;1&#xff09;需要将问题最小化&#xff0c;首先计算第i个位置最多容纳多少雨水&#xff08;细长的一条水柱&#xff09;&#xff0c;然后求和就是总的雨水量&#xff1b; &#xff08;2&#xff09;第i个位置容纳雨水量 min(左侧最高, 右…

【数据结构】单链表和双链表的基操实现

文章目录 一、链表的概念及结构二、链表的分类三、无头单向非循环链表1.单链表创建2.尾插和头插3.尾删和头删4.打印5.查找6.插入7.删除8.销毁 四、带头双向循环链表1.双链表的创建2.初始化3.判断链表是否为空4.尾插和头插5.尾删和头删6.查找7.插入8.删除9.销毁 五、总结链表和顺…

vulnhub靶场之FunBox-2

一.环境搭建 1.靶场描述 Boot2Root ! This can be a real life scenario if rockies becomes admins. Easy going in round about 15 mins. Bit more, if you are find and stuck in the rabbit-hole first. This VM is created/tested with Virtualbox. Maybe it works with…

【C++】vector的迭代器失效问题(什么是迭代器失效?那些操作会导致迭代器失效?如何避免迭代器失效?)

目录 一、前言 二、什么是迭代器失效&#xff1f; 三、哪些操作会导致迭代器失效&#xff1f; 四、如何避免迭代器失效&#xff1f; &#x1f95d; insert迭代器失效 ✨迭代器失效 ------ 扩容导致的野指针 ✨迭代器失效 ------ 迭代器指向的位置意义发生改变 &#x1f347…

揭秘无尘布:科技与清洁的完美结合

在现代科技的浪潮下&#xff0c;人们对于清洁工作的要求越来越高&#xff0c;尤其是对于精密仪器、光学设备、电子产品等高科技产品的清洁需求更为迫切。在这样的背景下&#xff0c;无尘布作为一种特殊的清洁工具&#xff0c;备受关注并得到广泛应用。本文将深入揭秘无尘布的科…

怎么口语外教一对一课程?这篇文章告诉你答案!

怎么口语外教一对一课程&#xff1f;在当今全球化的时代&#xff0c;英语口语能力已经成为许多人追求的重要技能。为了满足这一需求&#xff0c;市场上涌现出了许多提供一对一口语外教课程的软件。这些软件不仅提供了与母语为英语的外教进行实时交流的机会&#xff0c;还通过互…

德国韦纳WENAROLL滚压刀,液压缸,滚光刀,挤压刀,滚轧刀

德国韦纳WENAROLL滚压刀,液压缸&#xff0c;滚光刀,挤压刀&#xff0c;滚轧刀&#xff08;百度一下&#xff0c;西安尚融&#xff09; 德国韦纳&#xff08;WENAROLL&#xff09;的滚压刀、液压缸、滚光刀、挤压刀和滚轧刀在工业领域享有很高的声誉&#xff0c;这些产品因其高…

【深度学习实战(27)】「分布式训练」DDP单机多卡并行指南

一、DDP实现流程 &#xff08;1&#xff09;初始化进程 &#xff08;2&#xff09;model并行 &#xff08;3&#xff09;BN并行 &#xff08;4&#xff09;data并行 &#xff08;5&#xff09;进程同步 二、DDP代码实现 &#xff08;1&#xff09;初始化进程 #------------…

vivado Virtex 和 Kintex UltraScale+ 比特流设置

下表所示 Virtex 和 Kintex UltraScale 器件的器件配置设置可搭配 set_property <Setting> <Value> [current_design] Vivado 工具 Tcl 命令一起使用。

Vue 基础语法

【1】模板语法 &#xff08;1&#xff09;差值表达式 {{}}是 Vue.js 中的文本插值表达式。 它用于在模板中输出数据或表达式的值。当数据或表达式的值发生变化时&#xff0c;插值表达式会自动更新。 补充&#xff1a;三目运算符 它的基本语法是 Condition ? A : B&#xff0…

python绘图(pandas)

matplotlib绘图 import pandas as pd abs_path rF:\Python\learn\python附件\pythonCsv\data.csv df pd.read_csv(abs_path, encodinggbk) # apply根据多列生成新的一个列的操作&#xff0c;用apply df[new_score] df.apply(lambda x : x.数学 x.语文, axis1)# 最后几行 …

3W 3KVDC 隔离单输出 DC/DC 电源模块——TPG-3W 系列

TPG-3W系列是一款额定功率为3W的隔离产品&#xff0c;国际标准引脚&#xff0c;宽范围工作、温度–40℃ 到 105℃&#xff0c;在此温度范围内都可以稳定输出3W&#xff0c;并且效率非常高&#xff0c;高达88%&#xff0c;同时负载调整率非常低&#xff0c;对于有输出电压精度有…

61-ARM与FPGA间SPI通信电路设计

视频链接 ARM与FPGA间SPI通信电路设计01_哔哩哔哩_bilibili ARM与FPGA间SPI通信电路设计 第22课&#xff1a;SPI Flash 电路设计 第65课&#xff1a;实战S1-FPGA板级实战导学 1、SPI简介 1.1、SPI概述 SPI是(Serial Peripheral Interface) 串行外设接口&#xff0c;由Mot…

产业空间集聚DO指数计算

1.前言 创始人 :Duranton and Overman&#xff08;2005&#xff09; 目前应用较多的产业集聚度量指数主要基于两类&#xff0c;一是根据不同空间地理单元中产业经济规模的均衡性进行构造&#xff0c;如空间基尼系数与EG指数&#xff1b;二是基于微观企业地理位置信息形成的产业…

求解亲和数

【问题描述】 古希腊数学家毕达哥拉斯在自然数研究中发现&#xff0c;220的所有真约数&#xff08;即不是自身 的约数&#xff09;之和为&#xff1a; 1245101120224455110284。而284的所有真约数为1、2、4、71、142&#xff0c;加起来恰好为220。人 们对这样的数感到很惊奇&am…