算法与数据结构(五)--希尔排序

本文详细介绍了希尔排序算法,一种改进的插入排序方法。通过逐步减少增量序列进行部分排序,最终完成整个序列的排序。文章还提供了具体的实现代码示例。

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

希尔排序又称为“缩小增量排序”,是一种插入排序算法,是对直接插入排序的一种改进。

思想:

先按照一定的顺序跳着选元素序列进行插入排序,直到整个序列基本有序,再完整地调用一次插入排序。因为前面是跳着选元素的,就是按照一定的增量进行挑选元素,并且,这个增量是逐渐减小的,由于是跳着选的,所以,元素移动地速度就要比直接插入排序来得快,算法的时间复杂度也会明显降低。这里要注意,所取的增量序列中出了最后一个“1”以外,其他数必须是互质的,并且是按降序排列的。为什么呢?如果不是互质,就会出现都某个位置的数重复排序,这是没有必要的。

分解:

对于每一个增量n,原序列被拆分成n个序列:

(a[1] a[n+1] a[2n+1] ...)

(a[2] a[n+2] a[2n+2] ...) 

(a[3] a[n+3] a[2n+3] ...) 

...

(a[n] a[2n] a[3n] ...) 

而上面的每一个序列的排序过程又可以拆分成依次插入排序2个、3个、4个的形式。

(a[1] a[n+1] ) (a[1] a[n+1] a[2n+1] ) (a[1] a[n+1] a[2n+1] ...)

(a[2] a[n+2] (a[2] a[n+2] a[2n+2] ) (a[2] a[n+2] a[2n+2] ...)

(a[3] a[n+3] )  (a[3] a[n+3] a[2n+3] ) (a[3] a[n+3] a[2n+3] ...)

...

(a[n] a[2n] (a[n-1] a[2n-1] a[3n] )   (a[n-1] a[2n-1] a[3n] ...) 

那么,仔细观察上述结构可以发现,上面每个序列的最后一个元素的下标按照从上到下、从左往右的顺序看,是连续的。那么,为了编程实现的方便,对于每一个n,我们都是从a[n+1]开始,一直到数组结束a[end,]找出前面与其间隔n个所有元素进行插入排序即可。这样,就可以很好的简化了整个排序过程的实现。


小插曲:

写代码的时候,一开始是打算传递整个数组进去,以int a[]作为形参,但是,发现,这样必须在返回一个数组作为返回参数。这样子的话,每执行一次,还要将返回值进行

复制到a[],会有额外的开销,所以,考虑使用数组的引用。一开始写成int &a[],一运行,报错了。查了下《C++ Primer》,下标操作比引用具有更高的优先级,所以要在前面加括号,并且必须指定数组的个数,就是写成int  (&a)[11]。但是,这样写似乎太过死板了,以后代码就只能够是用于10个数的排序了。怎么办?

最后,发现的利器是模板。就是将数组的元素个数以模板形式传入,这样,这个参数既是指定的,又是可以根据函数的实际调用情况来确定,真的很巧妙。

下面是代码实现:


#include <iostream>
using namespace std;
template<size_t N> void ShellInsert(int (&a)[N],int dk)//希尔插入排序过程
{
	int alength = sizeof(a)/sizeof(a[0])-1;
	for (int i= dk+1;i<=alength;i++)
	{
		if (a[i]<a[i-dk])
		{	a[0] = a[i];
		    int j;
			for ( j=i-dk;j>0&&a[0]<a[j];j-=dk)
			{
				a[j+dk] = a[j];
			}
			a[j+dk] = a[0];
		}
	}
}

template<size_t N> void ShellSort(int (&a)[N],int dklist[],int listlen)
{
	for (int i=0;i<listlen;i++)
	{
		ShellInsert(a,dklist[i]);
	}
}

void main()
{
	int a[] ={0,2,5,6,9,8,4,3,1,6,0};//a[0] 是哨兵
	int dklist[] = {5,3,1};
	ShellSort(a,dklist,3);
	for (int i = 1;i<11;i++)
	{
		cout<<a[i]<<endl;
	}

}

当然,也可以用直接传入一个指针的形式来传入参数的,就是int *a。
#include <iostream>
using namespace std;

void ShellInsert(int *a,int dk,int alength)//希尔插入排序过程
{
	//int alength = 10;//sizeof(a)/sizeof(a[0])-1;
	for (int i= dk+1;i<=alength;i++)
	{
		if (a[i]<a[i-dk])
		{	
			a[0] = a[i];
			int j;
			for ( j=i-dk;j>0&&a[0]<a[j];j-=dk)
			{
				a[j+dk] = a[j];
			}
			a[j+dk] = a[0];
		}
	}
}

void ShellSort(int *a,int alength,int dklist[],int listlen)
{
	for (int i=0;i<listlen;i++)
	{
		ShellInsert(a,dklist[i],alength);
	}
}

void main()
{
	int a[] ={0,2,5,6,9,8,4,3,1,6,0};//a[0] 是哨兵
	int dklist[] = {5,3,1};
	ShellSort(a,10,dklist,3);
	for (int i = 1;i<11;i++)
	{
		cout<<a[i]<<endl;
	}

}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值