排序——希尔排序(Shell‘s Sort)

系列文章:
初学者必学的四种排序
排序——选择排序
排序——冒泡排序
排序——插入排序(Insertion sort)
排序——希尔排序

核心思想

⭐⭐在讲插入排序时说到当待排序序列本身就是相对有序的情况下,插入排序的效率是比较高的。希尔排序的核心就是尽量提高序列的有序性,再对整个序列进行一次插入排序。

⭐⭐问题也就是如何提高序列的有序性。讲这个问题之前首先看一下什么叫基本有序
对于一个序列

{9,1,5,8,3,7,4,6,2}

我们把它分成三组

{9,1,5} {8,3,7} {4,6,2}

每个组内进行一个排序,得到

{1,5,9,3,7,8,2,4,6}

这个序列还是杂乱无章的,但是它得有序性比之前已经提高了很多。再进一步像

{2,1,3,6,4,7,5,8,9}

⭐⭐这样的满足较小的数基本在前面,较大的数基本在后面的序列就可以称作基本有序了。知道什么叫基本有序之后,我们再看希尔排序中是如何做到使序列基本有序的。

⭐⭐我们设置一个增量(gap),比如增量为3,那么我们就把下标为0,3,6,9…的元素作为一个组进行插入排序,同样还有1,4,7,10…和2,5,8,11…一共三个组。三个组都内部进行插入排序之后,整个序列的有序性就得到了提高,当然,做到这样还远远不够。希尔排序中的增量(gap)是可变的,使得待排序序列进行了多次分组,多次部分排序,且最后gap将等于1使得序列进行了一次完整的插入排序。

⭐⭐gap的变化规律不固定,但最好gap不要成倍数变化(影响效率),且最后要等于1。
我习惯这样写:gap=gap/3+1(gap初值为待排序序列的长度),例如对于序列{5,4,3,2,1}
gap的值为:5,2,1

动图演示

图片摘自链接
在这里插入图片描述

代码实现(Java)

//增量选取:h=3h+1(1,4,7......)
public class 希尔排序 {
    public static void main(String[] args) {
        int a[]={45,12,89,56,23,45,789,1};
        for(int i=0;i<a.length;i++){
            System.out.print(a[i]+" ");
        }
        sortArray(a);
        System.out.println();
        for(int i=0;i<a.length;i++){
            System.out.print(a[i]+" ");
        }
    }
    //希尔排序
    public static void sortArray(int[] a){
        int gap=a.length;//间隔
        while(gap>=1) {
            gap = gap/ 3 + 1;
            //控制分组
            for(int k=0;k<gap;k++){
                //组内进行“插入排序”
                for(int i=k;i<a.length;i+=gap){
                    insertSort(a,i,gap);
                }
            }
            if(gap==1){
                /*当间隔等于1时,相当于直接插入排序
                * 完成直接插入排序后就可以退出了*/
                break;
            }
        }
    }
    public static void insertSort(int[] a,int i,int gap){
        int j=0;
        for(j=i+gap;j>0&&i>=0&&j<a.length;j-=gap){
            if(a[j]<a[i]){
                int temp=a[j];
                a[j]=a[i];
                a[i]=temp;
            }
            i-=gap;
        }
    }

}

代码实现(c语言)

void ShellSort(SqList *L) 					
{
	int i,j,k=0;
	int increment=L->length;
	do
	{
		increment=increment/3+1;			
		for(i=increment+1;i<=L->length;i++)
		{
			if (L->r[i]<L->r[i-increment])	
			{ 
				L->r[0]=L->r[i]; 			
				for(j=i-increment;j>0 && L->r[0]<L->r[j];j-=increment)
					L->r[j+increment]=L->r[j]; 
				L->r[j+increment]=L->r[0]; 
			}
		}
	}
	while(increment>1);
}

性能分析

⭐⭐大多数情况下,它的复杂度是低于插入排序的。但是对希尔排序的时间复杂度分析其实很困难,在特定情况下可以准确的估算排序码的比较次数和元素移动的次数,但要想弄清楚排序码比较次数和元素移动次数与增量选择之间的依赖关系,并给出完整的数学分析,还没有人能够做到。另外要知道希尔排序的复杂度是依赖于gap的选取的,科学家找到过一些合适的gap序列,使得复杂度最低。

⭐⭐实际上并不需要过分纠结于此,好好体悟其中的原理就可以,因为实际工程中,包括冒泡,选择,希尔等排序算法使用的都不多,我们学习主要是为了把这种思想方法内化到我们自身,提高我们的综合能力。
下一篇:用模板写递归
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值