排序算法4---希尔排序

Posted 干货频道

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了排序算法4---希尔排序相关的知识,希望对你有一定的参考价值。

前一节分析了插入排序,我们知道插入排序的优化改进算法为希尔排序,希尔排序是按照不同步长(下文中会交叉使用步长、步进、增量,实际是一个意思)对元素进行插入排序,起初元素很无序时,步长比较大,插入操作少,速度快;当数组基本有序时,步长比较小。

希尔排序的实质就是分组插入排序,是基于插入排序的,要了解希尔排序,必须先学习的原理。

算法原理


根据某一规则设置增量(步进);

以此增量对序列分组进行插入排序;

重复上一步,直到同一增量的所有分组完成排序;

重复上面的3个步,直到增量为 1 ,完成所有排序。


算法分析


下面以数列 a[] =  {6,3,5,8,1,4,7,2} 为例,来分析希尔排序。

分析:希尔排序首先需要增量的设置,这里按照 n / 2 进行设置,第二趟在前一次的基础上再除 2,以此类推,直到增量为 1。

第一趟:设置增量为 8 / 2 = 4,可以看到原始序列被分为4组 {6,1}, {3,4}, {5,7}, {8,2},接着单独对每个分组序列进行插入排序 {1,6}, {3,4},{5,7}, {2,8},得到序列{1,3,5,2,6,4,7,8}

第二趟:设置增量为 4 / 2 = 2,可以看到上一趟排完的序列被分为2组 {1,5,6,7} ,{3,2,4,8} , 接着单独对每个分组序列进行插入排序 {1,5,6,7} ,{2,3,4,8} ,得到序列{1,2,5,3,6,4,7,8}

第三趟:设置增量为 2 / 2 = 1,可以看到上一趟排完的序列被分为1组{1,2,5,3,6,4,7,8},接着单独对每个分组序列进行插入排序 {1,2,3,4,5,6,7,8},得到序列{1,2,3,4,5,6,7,8}

可以看到最后一趟排序之前,序列已经基本有序了,只有少量元素无序,最后一趟使用插入排序进行简单的微调,不再需要大量的移动操作就可以完成排序。


算法实现


在插入排序的基础上,我们实现以下算法(先弄清楚插入排序),注意将下面的算法实现与插入排序对比。


// 希尔排序

void shell_sort(int *arr, int n)

{

        int i = 0, j = 0;

int step = 0;

int val = 0;

        /* 这里设置步进*/

for(step = n / 2; step > 0; step /= 2) 

{

/*注意和插入排序对比一下*/

for(i = step; i < n; i++)           

{

val = arr[i];

j = i - step;

while(j >= 0 && arr[j] > val)

{

arr[j+step] = arr[j];

j -= step;

}

arr[j+step] = val;

}

}

}


算法改进


对于希尔排序来说,设置增量是没有标准的,我们可以先分析序列的特点来设置相应的增量,提高排序效率。



算法性能


时间复杂度

希尔排序的时间复杂度是取决于步长的,分析希尔排序的时间复杂度一般比较困难。总体来说比插入排序的效率高。插入排序在对几乎已经有序的序列排序时效率比较高,希尔排序对中等规模的序列排序效率比较高。


空间复杂度

希尔排序的空间复杂度为 o(1)。希尔排序的插入操作需要先取出待插入元素存放在1个临时空间,所以希尔排序的空间复杂度为 o(1)。


稳定性

希尔排序是一种不稳定的排序算法。虽然插入排序是稳定的,但是希尔排序并不是稳定的,因为插入排序每次只能将数据移动一个位置,所以插入排序是稳定的,而希尔排序是跳跃插入的,有可能改变原有序列里相同元素的前后顺序。


github源码连接:https://github.com/yqwung/sort



以上是关于排序算法4---希尔排序的主要内容,如果未能解决你的问题,请参考以下文章

经典排序算法——希尔排序

4希尔排序

常见排序算法-希尔排序

算法希尔排序 推导方法

排序算法-希尔排序

经典排序算法——希尔排序