希尔排序是对插入排序的一种优化。对插入排序不熟悉的同窗能够参考插入排序一文。html
插入排序中每次比较以后只能将数据挨着移动一位,所以效率并不高。可是插入排序对于几乎已经排好序的数据操做时,效率是很高的。希尔排序的思想就是针对这两点作了优化,使每一次比较以后元素能够跨过多个数据移动,从而提升了总体效率。优化方式是对要排序的元素进行分组,而后在每一个分组内进行插入排序。web
这样说仍是比较抽象,咱们用一个类比来讲明原理。假设一行人要从低到高排队,不妨设10我的吧,步骤以下:shell
回头再看一下以上步骤,不难发现其实以上6步就是分组、插入排序的反复循环,因而咱们能够获得希尔排序的通常步骤:数组
那这里问题就来了,这个增量怎么取呢?希尔同窗当年是初次取序列的一半为增量,之后每次减半,直到增量为1。方法简单直接,也能达到效果。app
下面咱们按照原始的希尔排序来用代码实现。考虑到希尔排序是插入排序的改进,而插入排序是能够原址排序的,因此不须要另外再开辟空间。svg
下面就是用C语言实现的代码。函数
void shell_sort(int a[], int n) { if(n<=0) return; int i, j, key; int d = n/2; //以d为增量进行分组 while (d > 0) { /* 对组内元素进行插入排序 */ for (j=d; j<n; j++) { //分别向每组的有序区域插入 key = a[j]; //插入a[j]到该组的有序区 i = j-d; //a[j-d]是a[j]所在组的有序区的最后一个元素 while( i>=0 && a[i]>key ) { a[i+d] = a[i]; //后移 i -= d; } a[i+d] = key; //插入 } d = d/2; //减少d以进行下一次分组 } }
为了验证此函数的效果,加上了以下辅助代码,对3个数组进行排序,运行结果在最后,可见排序成功。优化
#include <stdio.h> #include <stdlib.h> #define SIZE_ARRAY_1 5 #define SIZE_ARRAY_2 6 #define SIZE_ARRAY_3 20 void shell_sort(int a[], int n); void show_array(int a[], int n); void main() { int array1[SIZE_ARRAY_1]={1,4,2,-9,0}; int array2[SIZE_ARRAY_2]={10,5,2,1,9,2}; int array3[SIZE_ARRAY_3]; for(int i=0; i<SIZE_ARRAY_3; i++) { array3[i] = (int)((40.0*rand())/(RAND_MAX+1.0)-20); } printf("Before sort, "); show_array(array1, SIZE_ARRAY_1); shell_sort(array1, SIZE_ARRAY_1); printf("After sort, "); show_array(array1, SIZE_ARRAY_1); printf("Before sort, "); show_array(array2, SIZE_ARRAY_2); shell_sort(array2, SIZE_ARRAY_2); printf("After sort, "); show_array(array2, SIZE_ARRAY_2); printf("Before sort, "); show_array(array3, SIZE_ARRAY_3); shell_sort(array3, SIZE_ARRAY_3); printf("After sort, "); show_array(array3, SIZE_ARRAY_3); } void show_array(int a[], int n) { if(n>0) printf("This array has %d items: ", n); else printf("Error: array size should bigger than zero.\n"); for(int i=0; i<n; i++) { printf("%d ", a[i]); } printf("\n"); }
运行结果:spa
Before sort, This array has 5 items: 1 4 2 -9 0 After sort, This array has 5 items: -9 0 1 2 4 Before sort, This array has 6 items: 10 5 2 1 9 2 After sort, This array has 6 items: 1 2 2 5 9 10 Before sort, This array has 20 items: 13 -4 11 11 16 -12 -6 10 -8 2 0 5 -5 0 18 16 5 8 -14 4 After sort, This array has 20 items: -14 -12 -8 -6 -5 -4 0 0 2 4 5 5 8 10 11 11 13 16 16 18
从代码看,希尔排序用了三层循环,但它的时间复杂度却不是 ,由于每层循环的量级并非 。事实上,在最坏的状况下,希尔排序的时间复杂度也就是 。但通常状况却很差估计,由于其依赖于增量序列的取法。.net
前面咱们说了希尔同窗当年直接用了简单的方法取增量序列:初次取序列的一半为增量,之后每次减半,直到增量为1。然而这种取法在一些特殊状况下,会有效率问题。
举一个简单的例子:好比4个数[1,3,2,4]用希尔排序。第一步取增量为4/2=2,分组结果:1,2为一组,3,4为一组,组内排序,结果仍是[1,3,2,4]。发现没?通过一轮排序,竟然一点效果都没有,纯属浪费时间。
因而针对这个问题,有一些大佬们就开始改进增量的取法。其中一个叫Hibbard的大佬把增量序列的取法改成 ,从而避免了前面例子所遇到的状况,提升了希尔排序的效率。固然其余大佬还有其余大佬的一些方法,整体原则是应该尽可能避免序列中的值互为倍数的状况。
因此希尔排序的时间复杂度是不定的,如果取Hibbard增量序列,最坏的状况是 。
由于希尔排序直接在原址进行,不须要另外的空间,因此空间复杂度是 。