希尔排序,也称递减增量排序算法,是插入排序的一种高速而稳定的改进版本。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率
但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位
原始的算法实现在最坏的情况下需要进行O(n2)的比较和交换。V. Pratt的书[1] 对算法进行了少量修改,可以使得性能提升至O(n log2 n)。这比最好的比较算法的O(n log n)要差一些。
希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。
假设有一个很小的数据在一个已按升序排好序的数组的末端。如果用复杂度为O(n2)的排序(冒泡排序或插入排序),可能会进行n次的比较和交换才能将该数据移至正确位置。而希尔排序会用较大的步长移动数据,所以小数据只需进行少数比较和交换即可到正确位置。
一个更好理解的希尔排序实现:将数组列在一个表中并对列排序(用插入排序)。重复这过程,不过每次用更长的列来进行。最后整个表就只有一列了。将数组转换至表是为了更好地理解这算法,算法本身仅仅对原数组进行排序(通过增加索引的步长,例如是用i += step_size
而不是i++
)。
1 // 参考白话经典算法之希尔排序的思想 2 #include <stdio.h> 3 4 void SwapValue(int *OperatorA, int *OperatorB) 5 { 6 if ((NULL == OperatorA) || (NULL == OperatorB)) 7 { 8 printf ("Invalid Parameter(s)!\n"); 9 return; 10 } 11 12 if ((*OperatorA) != (*OperatorB)) 13 { 14 *OperatorA ^= *OperatorB; 15 *OperatorB ^= *OperatorA; 16 *OperatorA ^= *OperatorB; 17 } 18 } 19 20 void ShellSort(int *a, int N) 21 { 22 if ((NULL == a) || (N <= 0)) 23 { 24 printf ("Invalid Parameter(s)!\n"); 25 return; 26 } 27 28 int nGap = N >> 1; 29 30 while (nGap > 0) 31 { 32 for (int i = 0; i < nGap; ++i) 33 { 34 for (int j = i + nGap; j < N; j += nGap) 35 { 36 if (a[j] < a[j-nGap]) 37 { 38 int nTemp = a[j]; 39 40 for (int k = j - nGap; (k >= 0) && (a[k] > nTemp); k -= nGap) 41 { 42 SwapValue (&a[k], &a[k+nGap]); 43 } 44 } 45 } 46 } 47 48 nGap >>= 1; 49 } 50 } 51 52 // 优化一,从nGap开始依次往后循环,但是每个元素都只与自己组内的元素做插入排序 53 void ShellSort1(int *a, int N) 54 { 55 if ((NULL ==a) || (N <= 0)) 56 { 57 printf ("Invalid Parameter(s)!\n"); 58 return; 59 } 60 61 int nGap = N >> 1; 62 63 while (nGap > 0) 64 { 65 for (int i = nGap; i < N; ++i) 66 { 67 if (a[i] < a[i-nGap]) 68 { 69 int nTemp = a[i]; 70 71 for (int j = i - nGap; (j >= 0) && (a[j] > nTemp); j -= nGap) 72 { 73 SwapValue (&a[j], &a[j+nGap]); 74 } 75 } 76 } 77 78 nGap >>= 1; 79 } 80 } 81 82 int main(void) 83 { 84 int a1[9] = {9, 8, 7, 6, 5, 4, 3, 2, 1}; 85 int a2[9] = {9, 8, 7, 6, 5, 4, 3, 2, 1}; 86 87 ShellSort (a1, 9); 88 ShellSort1 (a2, 9); 89 90 for (int i = 0; i < 9; ++i) 91 { 92 printf ("%d ", a1[i]); 93 } 94 95 printf ("\n"); 96 97 for (int i = 0; i < 9; ++i) 98 { 99 printf ("%d ", a2[i]); 100 } 101 102 printf ("\n"); 103 104 return 0; 105 }