希尔排序

来源:互联网 发布:数据库的导入和导出 编辑:程序博客网 时间:2024/06/05 19:13

希尔排序其实是直接插入排序的一个拓展。虽然没有直接证明说明希尔排序比直接插入排序快,但是在并行运算上,希尔排序的确十分优秀。而且这是一个不稳定的排序算法。

为什么用希尔排序: 直接插入排序有个不足之处是当一个数组是十分无序的状态时,排序的时间复杂度较久。特别是要将一个逆序的数组排为顺序的数组,这个逆序的数组相当于完全无序的,而直接插入排序需要的时间久。(数据多时,且无序)而如何使排序时间减少呢,那么对数据有一定的要求,那就是基本有序。利用希尔排序,可以使数据先基本有序,然后在利用直接插入法。直接插入排序在数据量少时效率还是挺高的,所以就先将数组分开一小组一小组的排,最后使得整个数组是基本有序状态,再用一次直接插入排序法即可。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:
1,插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。
2,插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。

基本思想:其实是一种分组插入方法。希尔排序是把数据按下标的一定增量分组d={d1,d2,3,d4,…,dn=1},增量相当于分的组数,而数组长度/增量≈每组中的元素。对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的数据越来越多,当增量减至1时,所有数据恰被分成一组,算法便终止。

//主要函数:void shellSort(List *list, int d[], int num);   //希尔排序void shellInsert(List * list, int dk);          //分组直接插入排序List * initList(List * list, int len);          //初始化

初始化函数:为list分配空间,指定长度,然后给数组赋值随机数。

List * initList(List * list, int len){    list = (List *)malloc(sizeof(List));    list->length = len;    list->data = malloc(sizeof(int) * len);    srand((unsigned)time(NULL));    for (int i = 0; i < len; i++)    {        list->data[i] = rand() % 100;    }    return list;}

直接插入排序函数:
这里与直接插入排序函数没有太大区别,直接插入排序法可参考这篇blog:

http://blog.csdn.net/sherilindas/article/details/51893511

这里主要的区别是排序时进行了分组。
传入的参数为缩小增量,通过增量给整个数组分组。例如下图。
这时增量d=5,所以共有5组,每组有数组长度/增量d=2个。
这里写图片描述

于是就在每组中进行插入排序。循环重复条件直到length – d,也即是下图此处。
这里写图片描述

void shellInsert(List * list, int dk)   //dk即为增量 {    int temp = 0;            //temp设为哨兵位,即用于储存抽出来的元素    for (int i = 0; i < list->length - dk; i++)    {        //当此元素比此组的下一个元素要大时开始交换        if (list->data[i] > list->data[i + dk])        {            temp = list->data[i + dk];   //抽取小的元素            int j = i + dk;            //组内比temp小的元素全部往后移            while (j> 0&&temp < list->data[j - dk])            {                list->data[j] = list->data[j - dk];                j = j - dk;            }            //将temp放入前面            list->data[j] = temp;        }    }    return;}

而shellSort函数主要负责改变增量,随着增量的减小,每组中的数据会增加。直到增量变为1,这时就只有一组数据,这时整个数组已经基本有序。

void shellSort(List *list, int d[], int num)  //d为增量序列,num为增量序列长度{    for (int i = 0; i < num; i++)      {        shellInsert(list, d[i]);  //每次循环改变增量    }    return;}

这里举一个例子:假如数组为{49,38,65,97,76,13,27,49,55,04},数组长度为10,增量序列为d={5,3,1}
第一趟增量d=5,分为5组,分别进行直接插入排序。
这里写图片描述
最后结果为(13,27,49,55,04,49,38,65,97,76)
第二趟增量d=3,分为3组,分别进行直接插入排序。
这里写图片描述
最后结果为(13,04,49,38,27,49,55,65,97,76)
第三趟增量d=1,分为1组,这是数组已经基本有序,直接插入排序。
这里写图片描述

以下为源文件:

#include <stdio.h>#include <stdlib.h>#include <time.h>typedef struct List{    int *data;    int length;}List;void shellSort(List *list, int d[], int num);void shellInsert(List * list, int dk);List * initList(List * list, int len);void shellSort(List *list, int d[], int num){    for (int i = 0; i < num; i++)    {        shellInsert(list, d[i]);    }    return;}void shellInsert(List * list, int dk){    int temp = 0;    for (int i = 0; i < list->length - dk; i++)    {        if (list->data[i] > list->data[i + dk])        {            temp = list->data[i + dk];            int j = i + dk;            while (j> 0&&temp < list->data[j - dk])            {                list->data[j] = list->data[j - dk];                j = j - dk;            }            list->data[j] = temp;        }    }    return;}List * initList(List * list, int len){    list = (List *)malloc(sizeof(List));    list->length = len;    list->data = malloc(sizeof(int) * len);    srand((unsigned)time(NULL));    for (int i = 0; i < len; i++)    {        list->data[i] = rand() % 100;    }    return list;}void printList(List * list){    for (int i = 0; i < list->length; i++)    {        printf("%d ", list->data[i]);    }    printf("\n\n");    return;}int main(){    List * list = NULL;    int d[3] = { 5,3,1 };    list = initList(list, 10);    printList(list);    shellSort(list, d, 3);    printList(list);    return 0;}
0 0