希尔排序

希尔排序是一种改进的插入排序算法,通过增量序列分组进行排序。文章介绍了希尔排序的原理、gap步长选择的重要性、算法实现及时间复杂度分析,强调了增量序列选择对排序效率的影响,展示了希尔排序在效率上优于直接插入排序的特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考:希尔排序希尔排序学习

希尔排序介绍

希尔排序(Shell Sort)是插入排序的一种,它是针对直接插入排序算法的改进。该方法又称缩小增量排序,因DL.Shell于1959年提出而得名。

希尔排序实质上是一种分组插入方法。它的基本思想是:对于n个待排序的数列,取一个小于n的整数gap(gap被称为步长)将待排序元素分成若干个组子序列,所有距离为gap的倍数的记录放在同一个组中;然后,对各组内的元素进行直接插入排序。 这一趟排序完成之后,每一个组的元素都是有序的。然后减小gap的值,并重复执行上述的分组和排序。重复这样的操作,当gap=1时,整个数列就是有序的。

希尔排序说明

假设待排序文件有10个记录,其关键字分别是:
        49,38,65,97,76,13,27,49,55,04。
增量序列的取值依次为:
        5,3,1

希尔排序动态模拟演示

希尔排序gap步长的选取

增量序列的选择
     Shell排序的执行时间依赖于增量序列。
     好的增量序列的共同特征:
  ① 最后一个增量必须为1;
  ② 应该尽量避免序列中的值(尤其是相邻的值)互为倍数的情况。
     有人通过大量的实验,给出了目前较好的结果:当n较大时,比较和移动的次数约在之间。

增量的选择影响到排序的时间!

希尔排序的实现

希尔排序的实现(算法:C语言实现 P172)

void shellSort(Item a[], int l, int r)
{
	int i, h, k;
	for(h=1; h<=(r-l)/9; h=3*h+1);		//根据待排序的元素数,决定希尔增量的最大值
	for(; h>0; h/=3)	//外层循环为增量递减
	{
		for(i=l+h; i<=r; i++)
		{
			int j=i;
			Item v = a[i];
			while(j>=l+h && less(v, a[j-h]))
			{
				a[j]=a[j-h];
				j -= h;
			}
			a[j] = v;
		}
	}	
}
Ps:这里采用的希尔增量是从1开始,通过乘3加1得到下一个步长。但在程序实现中,是先通过l和r来确定增量的最大值。

网上通用的希尔增量是2的倍数,如1 2 4 8 16 32 64……,但是书上说,这是不好的步长序列,直到最后一遍,步长为1时,在奇数位置的元素才会与偶数位置上的元素进行比较。

只需要修改上面程序中产生步长的外部循环。

希尔排序的完成实现代码

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

typedef int Item;
#define key(A) (A)
#define less(A, B) (key(A)<key(B))
#define exch(A, B) {Item t = A; A = B; B = t;}
#define compexch(A, B) if(less(B, A)) exch(A, B)	//最后A<B
/*
希尔排序:i下标元素前面是已经排过序的
*/
void selection(Item a[], int l, int r)
{
	int i, j;
	for(i=l; i<r; i++)
	{
		int min = i;	//用于存放选择时候扫描的最小值
		for(j=i+1; j<=r; j++)
		{
			if(less(a[j], a[min])) min=j; 
		}
		exch(a[i], a[min]);
	}
}
void shellSort(Item a[], int l, int r)
{
	int i, h, k;
	for(h=1; h<=(r-l)/9; h=3*h+1);		//根据待排序的元素数,决定希尔增量的最大值
	for(; h>0; h/=3)	//外层循环为增量递减
	{
		for(i=l+h; i<=r; i++)
		{
			int j=i;
			Item v = a[i];
			while(j>=l+h && less(v, a[j-h]))
			{
				a[j]=a[j-h];
				j -= h;
			}
			a[j] = v;
		}
		printf("\n增量为%d,排序后\n", h);
		for(k=l; k<r; k++)
		{
			printf("%d ", a[k]);
		}
	}	
}

int main(void)
{
	int i, N, sw;	
	N = 50;
	sw = 1;
	int *a = (int *)malloc(N*sizeof(int));	//为数据a分配相应的存储空间
	if (sw)
	{
		for(i=0; i<N; i++)
		{
			a[i] = 1000 * (1.0 * rand()/RAND_MAX);
		}
	}
	else
	{
		for (i = 0; i < N; i++)
		{
			scanf("%d", &a[i]);
		}
	}
	puts("排序前");
	for(i=0; i<N; i++)
	{
		printf("%d ", a[i]);
	}
	shellSort(a, 0, N-1);
	puts("\n排序后");
	for(i=0; i<N; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
	return 0;
}

希尔排序的时间复杂度和稳定性分析

希尔排序时间复杂度

希尔排序的时间复杂度与增量(即,步长gap)的选取有关。例如,当增量为1时,希尔排序退化成了直接插入排序,此时的时间复杂度为O(N²),而Hibbard增量的希尔排序的时间复杂度为O(N3/2)。

希尔排序稳定性

希尔排序是不稳定的算法,它满足稳定算法的定义。对于相同的两个数,可能由于分在不同的组中而导致它们的顺序发生变化。
算法稳定性 -- 假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的!

希尔排序的分析

Shell排序的时间性能优于直接插入排序
     希尔排序的时间性能优于直接插入排序的原因:
  ①当文件初态基本有序时直接插入排序所需的比较和移动次数均较少。
  ②当n值较小时,n和的差别也较小,即直接插入排序的最好时间复杂度O(n)和最坏时间复杂度0()差别不大。
  ③在希尔排序开始时增量较大,分组较多,每组的记录数目少,故各组内直接插入较快,后来增量逐渐缩小,分组数逐渐减少,而各组的记录数目逐渐增多,但由于已经按作为距离排过序,使文件较接近于有序状态,所以新的一趟排序过程也较快。
     因此,希尔排序在效率上较直接插人排序有较大的改进。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值