希尔排序
它的实质是分组插入排序,又称缩小增量排序,它是基于插入排序的思想
直接插入排序对于基本有序的数组,且基数N较小的情况下性能最好,希尔排序就是利用直接插入排序的这个特点,将较大的数组拆分为较小的子序列进行。
思想
先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比单纯的直接插入排序有较大提高。
图形描述:
我们以以n=10的一个数组 {49, 38, 65, 97, 26, 13, 27, 49, 55, 7}为例来走一下希尔排序的过程:
首先 gap = 10 / 2 = 5,整个数组被分成 5 组,分组后的子数组分别是:{49,13},{38,27},{65,49},{97,55},{26,7},子数组进行直接插入排序后为: {13,49},{27,38},{49,65},{55,97},{7,26}。
此时 gap = 5 / 2 = 2,整个数组被分成 2 组,分组后的子数组是:{13,49,7,38,97},{27,55,49,65,26},子数组进行直接插入排序后为:{7,13,38,49,97},{26,27,49,55,65}。
现在 gap = 2 / 2 = 1,所以直接对整个数组进行直接插入排序,至此整个希尔排序完成。
void ShellSort(int a[],int n)
{
int gap,j;
for (gap = n / 2; gap > 0; gap /= 2) //控制步长
for (j = gap; j < n;++j) //++j操作是很巧妙地,这步操作省去了对数组进行分组的一层循环
{
int tmp = a[j];
int prev = j - gap;
while (prev>=0 && a[j]<a[prev]){ //这里是直接插入排序的核心步骤
a[j] = a[prev];
prev -= gap;
}
a[prev + gap] = tmp;
}
}
时间复杂度和空间复杂度:
时间复杂度:希尔排序的时间复杂度较其他排序没有那么直观,一般认为它的时间复杂度为,比直接插入要好。 希尔排序对于中等规模(N <= 1000)的序列具有较高的效率。
空间复杂度:O(1)
稳定性:
是不稳定的,在排序过程中,相同关键字的领先关系有可能会发生变化。
例如,有待排序序列为{2,4,1,2},设 gap = 2, 排序后的结果为{1,2,2,4}。
增量的取法:
最初希尔提出 gap = N / 2,再取 gap = gap / 2,直到 gap = 1 为止,这种方案的缺点是,奇数位置的元素在最后一步才会与偶数位置的元素进行比较,使得希尔排序效率降低。因此后来 knuth 提出 gap = N / 3 +1 的方案,没有给出证明。已知的最好步长序列是由Sedgewick提出的(1, 5, 19, 41, 109,…),该序列来自个算式。关于这点说到这里了。。。