当前位置: 代码网 > it编程>前端脚本>Powershell > 【数据结构与算法】希尔排序:基于插入排序的高效排序算法

【数据结构与算法】希尔排序:基于插入排序的高效排序算法

2024年07月28日 Powershell 我要评论
希尔排序(Shell Sort)是插入排序的一种更高效的改进版本,也称为缩小增量排序。希尔排序由Donald Shell于1959年提出,并在其发表的论文“A high-speed sorting procedure”中详细描述了该算法。希尔排序的直接灵感来源于插入排序,但它在插入排序的基础上进行了显著的改进,旨在提高排序效率,特别是针对大规模数据集。

目录

一、引言 

二、基本原理

三、实现步骤

四、c语言实现

五、性能分析

1. 时间复杂度:近似为o(nlog2n)

2. 空间复杂度:o(1)

3. 稳定性:不稳定的

六、优化

七、应用场景


一、引言 

二、基本原理

三、实现步骤

 
1.外循环进行多轮预排序
选择一个变量序列:

这个序列是逐渐减小的,gap的值较大时,数据可以更快的前后变动,但不容易"基本有序";gap较小时数据前后变动较慢,但更接近"基本有序"。  通常可以选取gap = n/3, gap = gap/3, ...,直到gap= 1。

但是要注意,如果直接每次都/3,可能面临的情况就是最后一组gap的值跳过了1,比如n=8时,gap第一次等于2,第二次等于0,解决方法也很简单,gap每次不是/3,而是gap=gap/3+1,就可以让gap最后一次一定会减小到1

2.第二层循环,每一轮预排序中进行分组
按gap进行分组:根据当前的变量gap,将待排序的数组元素下标按gap分组,总共可以分成gap组。比如gap为3时,每一组元素的首元素分别是0,1,2

3.第三层循环,分组之后,控制组里数据执行插入排序
每一组的数据有n/gap个,下标为0,gap, 2gap, 3gap,...的元素分为一组;下标为1,gap+1,2gap+1,3gap+1……的元素分为一组……


这一层循环一个需要注意的细节就是预防数组的越界:每一组分组数据的最后一个数据一般不会是数组的最后一个数据。每次选取的要插入的数据下标是end+gap,那么这个下标不能超过n-gap。比如数组有10个元素,gap为3,第一组数据最后一个数据的下标是9,要保证这一组数据访问到下标9之后,不再向后访问,因为下一次访问end为9,要插入的数据,9+gap的位置已经没有数据了。


4.第四层循环,实现插入排序的过程
每个数据向前扫描和移动,找到合适的位置后插入,直接在插入排序代码的基础上稍加修改即可

5.递减变量gap并重复上述分组排序过程:

每完成一轮按变量gap的分组排序后,将变量gap减小,然后重复分组排序过程,直到变量gap为1,此时整个数组恰好被分成一组,进行最后一次直接插入排序。
 

四、c语言实现

void shellsort1(int* a, int n)//希尔排序升序
{
	int gap = n;
	while (gap > 1)//多组预排序,最后一组gap==1为直接插入排序
	{
		gap = gap / 3 + 1;
		for (int i = 0; i <gap; i++)//控制分组的组数:gap组
		{
			for (int j = i; j < n - gap; j += gap)//控制每组的插入元素个数:n/gap个
			{
				int key = a[j+gap];
				int end = j;
				while (end >= 0 && a[end] > key)//比较和移动元素
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				a[end + gap] = key;//满足大小关系后插入到指定位置
			}
		}
	}
}

void shellsort2(int* a, int n)//希尔排序降序
{
	int gap = n;
	while (gap > 1)//多组预排序,最后一组gap==1为直接插入排序
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < gap; i++)//控制分组的组数:gap组
		{
			for (int j = i; j < n - gap; j += gap)//控制每组的插入元素个数:n/gap个
			{
				int key = a[j + gap];
				int end = j;
				while (end >= 0 && a[end] < key)//比较和移动元素
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				a[end + gap] = key;//满足大小关系后插入到指定位置
			}
		}
	}
}

五、性能分析

1. 时间复杂度:近似为o(nlog2n)

希尔排序的时间复杂度并不是一个定值,它依赖于增量序列(gap序列)的选取

  • 平均时间复杂度:在多数情况下,希尔排序的平均时间复杂度可以近似为o(nlog2n),但这并不是一个严格的结论,因为实际性能会受到增量序列的具体选择和数据初始状态的影响。
  • 最佳时间复杂度:在最佳情况下,即数据已经接近有序时,希尔排序可以接近线性排序的效率,但具体数值难以准确给出。
  • 最差时间复杂度:在最坏情况下,希尔排序的时间复杂度仍然是o(n^2)。这通常发生在增量序列选择不当,导致算法无法有效减少数据比较和移动次数时。

2. 空间复杂度:o(1)

希尔排序是原地排序算法,它只需要一个额外的空间来存储临时变量(用于数据交换),因此其空间复杂度为o(1)。这意味着希尔排序在排序过程中不会占用额外的存储空间,这对于内存资源有限的环境非常有利。

3. 稳定性:不稳定的

希尔排序是不稳定的排序算法。在排序过程中,由于存在跳跃式的比较和移动,相同元素的相对位置可能会发生变化。因此,在需要保持元素原始顺序的场景中,希尔排序可能不是最佳选择。

六、优化

void shellsort(int* a, int n)//希尔排序升序代码优化版(效率没有提升)
{
	int gap = n;
	while (gap > 1)//多组预排序,最后一组gap==1为直接插入排序
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < n - gap; i++)//控制多组以及插入元素个数
		{
			int key = a[i + gap];
			int end = i;
			while (end >= 0 && a[end] > key)//比较和移动元素
			{
				a[end + gap] = a[end];
				end -= gap;
			}
			a[end + gap] = key;//满足大小关系后插入到指定位置
		}
	}
}

七、应用场景

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com