希尔排序

1. 算法思想

希尔排序(Shell Sort),也称递减增量排序,是插入排序的一种高速而稳定的改进版本。希尔排序按其设计者希尔(Donald Shell)的名字命名,该算法由1959年公布。希尔排序是基于插入排序的如下性质而进行改进的:插入排序在对几乎已经排好序的数据操作时,效率非常高,可以达到线性排序的效率。

该方法的基本思想是:先将整个待排序列分成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上有较大提高。

假设有一个很小的数据在一个已按升序排好序的数组的末端。如果使用复杂度为O(n2)的排序(冒泡排序或插入排序)算法,可能会进行n次的比较和交换才能将该数据移至正确位置。而希尔排序会用较大的步长移动数据,所以小数据只需进行少数比较和交换即可到达正确位置。

例如,假设有一组数:[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ]。如果以步长为5开始排序,可以将这组数放在有5列的表中来更好地描述算法:

13 14 94 33 82
25 59 94 65 23
45 27 73 25 39
10

然后对每进行排序:

10 14 73 25 23
13 27 94 33 39
25 59 94 65 82
45

这时顺序读取每行数据得到:[ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ]。这时10已经移至正确位置了,然后再以3为步长进行排序:

10 14 73
25 23 13
27 94 33
39 25 59
94 65 82
45

排序之后变为:

10 14 13
25 23 33
27 25 59
39 65 73
45 94 82
94

最后再以1为步长进行排序,即整体进行一次直接插入排序。

2. 算法实现及测试:

#include <iostream>
using namespace std;

// 希尔排序
void ShellSort(int data[], int count)
{
    int step = 0;
    int auxiliary = 0;

    for (step = count / 2; step > 0; step /= 2)
    {
        // 从数组第step个元素开始
        for (int i = step; i < count; i++) 
        {
            // 每个元素与自己组内的数据进行直接插入排序
            if (data[i] < data[i - step]) 
            {
                auxiliary = data[i];
                int j = i - step;

                while (j >= 0 && data[j] > auxiliary)
                {
                    data[j + step] = data[j];
                    j -= step;
                }

                data[j + step] = auxiliary;
            }
        }
    }
}

int main()
{
    int array[] = {9, 6, 3, 8, 7, 1, 5, 2, 4};

    ShellSort(array, 9);

    for(int i = 0; i < 9; ++i)
        cout << array[i] << endl;

    return 0;
}

3. 步长序列

步长的选择是希尔排序的重要部分。只要最终步长为1,任何步长序列都可以工作。算法最开始以一定的步长进行排序。然后会继续以更小的步长进行排序,最终算法以步长为1进行排序。当步长为1时,算法变为插入排序,这就保证了数据一定会被排序。

Donald Shell 最初建议步长选择为n/2,并且对步长取半直到步长达到1。虽然这样取可以比O(n2)类的算法(插入排序)更好,但仍然有减少平均时间和最差时间的余地。

 

已知的最好步长序列由Marcin Ciura设计:(1,4,10,23,57,132,301,701,1750,…) 这项研究也表明“在希尔排序中最主要的操作是比较,而不是交换。”用该步长序列的希尔排序比插入排序和堆排序都要快,甚至在小数组中比快速排序还快,但是在涉及大量数据时希尔排序还是比快速排序慢。

另一个在大数组中表现优异的步长序列是(斐波那契数列除去0和1将剩余的数以黄金分割比的两倍的幂进行运算得到的数列):(1, 9, 34, 182, 836, 4025, 19001, 90358, 428481, 2034035, 9651787, 45806244, 217378076, 1031612713, …)

【学习资料】 《维基百科》 《http://blog.csdn.net/morewindows/article/details/6668714》

posted on 2012-12-31 10:05  zhuyf87  阅读(398)  评论(0编辑  收藏  举报

导航