希尔排序的介绍以及例子分析

什么是希尔排序

希尔排序是建立在插入排序基础上的一种,减少增量的一种算法(个人感觉就是减少分组)相对于单纯的插入排序,其经过一次次分组排序,其稳定性相对比较好。同时希尔算法在最坏的情况下和平均情况下执行效率相差不是很多。

个人理解:就是对n个数字分组,然后排序。假设有n个,那么第一个增量就是d=n/2,后面就是d=d/2,在各个组进行插入排序,最后d=1后,结束程序,排序结束。
(基础定义网上百科官方一打一打,所以就不多言)

看图了解

假设有n个(n=10个)乱序版的数字组成的数组:69,56,12,136,3,55,46,99,88,25
如下图
图1
如上图,d=10/2=5,在不超过长度的情况下每个元素的下标+5,组成一组,进行排序,比较大小。(就是分成5组比较大小,小的排前面,大的排后面)
排后结果如下图
图2
排完后,d=d/2=5/2=2(c语言整型,序列一般也不会有2.5。。。。)
相当于对其分成2组,然后排序。
图3
对上述两组内容进行排序,而不是两个两个比较,排序结果如下图
图4
然后d=d/2=2/2=1
在这里插入图片描述
所有数据都在一组,然后排序(由于前序操作基本上排的差不多了,基本上交换就在左右附近)
在这里插入图片描述
d=0,结束排序。

代码赏析

1.先赏析一个思维比较简单的,但是代码相对而言粗糙一点的

#include"stdio.h"
void xier(int a[],int n)
{
	int d,i,j,k,l,m,num;
	d=n/2;
	i=j=k=0;
	while(d>=1)//判断程序结束条件
	{
		for(i=0;i<d;i++)//确定分成几组
		{
			for(k=i;k<n;k=k+d)//确定哪些数字在一组
			{
				for(j=k+d;j<n;j=j+d)//开始比较
				{
					if(a[k]>a[j])//找到插入坐标
					{
						m=a[k];
						a[k]=a[j];
						for(num=j;num>k+d;num-=d)//数据后移
						{
							a[num]=a[num-d];
							
						}
						a[num]=m;
					}
				}
			}
		}
		d=d/2;
	}
}
void main()
{
	int a[10]={69,56,12,136,3,55,46,99,88,25};
	int i=0;
	xier(a,10);
	for(i=0;i<10;i++)
	{
		printf("%d	",a[i]);
	}
	printf("\n");
}

结果

gec@ubuntu:/mnt/hgfs/share-2$ gcc eight.c -o eight
gec@ubuntu:/mnt/hgfs/share-2$ ./eight
3	12	25	46	55	56	69	88	99	136

赏析:相对而言这个代码思维比较通俗点,首先是一个while循环判决,什么时候结束子程序,然后用一个for循环,保证每次增量d变换后,每组都循环比较完成,然后用一个for循环将各组内的数字进行排序,然后对组内的数据进行插入排序(首先针对组内元素一个一个排序,寻找应该插入的位置,然后插入,对其插入后原本有序的元素向后移)

总体就是:先判断是否结束——>然后分组——>插入排序

2.接下来我们赏析一个稍微稍微不寻常点的

#include <stdio.h>
int shsort(int s[], int n)    /* 自定义函数 shsort()*/
{
    int i,j,d;
    d=n/2;    /*确定固定增虽值*/
    while(d>=1)
    {
        for(i=d+1;i<=n;i++)    /*数组下标从d+1开始进行直接插入排序*/
        {
            s[0]=s[i];    /*设置监视哨*/
            j=i-d;    /*确定要进行比较的元素的最右边位置*/
            while((j>0)&&(s[0]<s[j]))
            {
                s[j+d]=s[j]; /*数据右移*/
                j=j-d;    /*向左移d个位置V*/
            }
            s[j + d]=s[0];    /*在确定的位罝插入s[i]*/
        }
        d = d/2;    /*增里变为原来的一半*/
    }
	return 0;
}
int main()
{
    int a[11],i;    /*定义数组及变量为基本整型*/
    printf("请输入 10 个数据:\n");
    for(i=1;i<=16;i++)
    scanf("%d",&a[i]);    /*从键盘中输入10个数据*/
    shsort(a, 16);    /* 调用 shsort()函数*/
    printf("排序后的顺序是:\n");
    for(i=1;i<=16;i++)
    printf("%5d",a[i]);    /*输出排序后的数组*/
    printf("\n");
    return 0;
}

结果

gec@ubuntu:/mnt/hgfs/share-2$ gcc 1.c -o 1
gec@ubuntu:/mnt/hgfs/share-2$ ./1
请输入 10 个数据:
12
13
14
16
17
22
1
2
3
4
排序后的顺序是:
    1    2    3    4   12   13   14   16   17   22

赏析:函数内也是一个while循环控制子程序的结束,d的相关操作也和上一个程序差不多,区别在于,一个是他没有利用int变量而是数组的首元素作为交换的存储,其次就是比较巧妙的 j=i-d; while((j>0)&&(s[0]<s[j])),第一次的时候,单独比较,看不出来什么,但是第二个d的时候,利用i的变化,对分组元素进行了不断比较。
假设已经进行了d=5的比较完成,将进行下一次
在这里插入图片描述

d=2;
i=d+1=3
s[0]=s[3]
j=3-2=1
然后判断是否需要交换,比较的仅仅只是s[1]和s[3],比较结束,j=1-2<0,结束循环比较,将s[0]的内容返还
i++——>i=4
j=4-2=2
然后判断是否需要交换,比较的仅仅只是s[2]和s[4],比较结束,j=2-2<=0,结束循环比较,将s[0]的内容返还
i++——>i=5
j=4-2=3
然后判断是否需要交换,比较的仅仅只是s[3]和s[5],比较结束,j=3-2>0,循环比较继续,j=1
因为s[3]和s[5]已经比较结束,所以s[3]中是其中小的,而且第一次比较时s[3]是最大的,所以比较s[1]和s[3]可以比较出最小的。然后将s[0]的内容返还。
剩下的就一样了。

结束语

谢谢观看,仅代表个人观点,欢迎留下不同意见!

猜你喜欢

转载自blog.csdn.net/qq_44885018/article/details/96595350