技术交流QQ群:1027579432,欢迎你的加入!

一.原理

  • 希尔排序是对直接插入排序的改进,建立在直接排序的基础上实现的。因为直接插入排序适合那些数据本身就是基本有序的或者数据量比较小的情况。但是,实际中数据量小或数据基本有序属于特殊情况,这就是直接插入排序的局限性。希尔排序的基本思想就是当有大量的数据需要排序时,可以将大量的数据分组成若干子序列,此时每个子序列的数据比较少,可以对每个子序列使用直接插入排序。当整个序列基本有序时(基本有序:小的关键字基本在前,大的基本在后,不大不小的基本在中间)。再对整体的数据进行一次直接插入排序。对原始数据进行分割的目的就是减少待排序数据的个数,使得整个序列朝着基本有序方向发展。采用的分割策略是:将相距某个增量increment的数据组成一个子序列,这样才能保证在子序列内分别进行直接插入排序后得到的结果是基本有序的。

二.希尔排序过程

希尔排序.jpg

三.希尔排序完整代码

#include "iostream"

        using namespace std;

        #define N 9
        #define MAXSIZE 10
        #define OK 1
        #define ERROR 0
        #define TRUE 1
        #define FALSE 0

        typedef int Status;
        typedef struct{
            int r[MAXSIZE+1];
            int len;
        }Sqlist;

        void show(Sqlist L){
            for(int i = 1; i <= L.len; i++)
                cout << L.r[i] << " ";
            cout << endl;
        }

        // 希尔排序
        void ShellSort(Sqlist *L){
            int i, j;
            int increment = L->len;  // 增量的初始化
            do{ // 每执行一次do-while循环就会生成一个子序列,每个子序列的生成使用的增量increment是不同的!但是序列将原来越来越朝着基本有序的方向发展
                increment = increment / 3 +1;
                for(i = increment+1; i <= L->len; i++){
                    if(L->r[i] < L->r[i-increment]){  // 插入的元素是L->r[i],进行比较
                        L->r[0] = L->r[i];   // 将需要的插入的元素暂时存储在哨兵中
                        for(j = i-increment; j>0 && L->r[j] > L->r[0];j-=increment)
                            L->r[j+increment] = L->r[j];   // 进行交换操作
                        L->r[j+increment] = L->r[0];
                    }  
                }
            }while(increment > 1);  // 跳跃式的移动,而直接排序中是使用increment=1固定的!
        }

        int main(){
            Sqlist L;
            int d[N] = {9, 1, 5, 8, 3, 7, 4, 6, 2};
            for(int i = 0; i < N; i++)
                L.r[i+1] = d[i];
            L.len = N;
            cout << "希尔排序前: ";
            show(L);
            cout << "希尔排序后: ";
            ShellSort(&L);
            show(L);
            return 0;
        }
  • STL实现
#include <iostream>
#include <vector>
#include <algorithm>


using namespace std;

//  希尔排序:每一轮按照事先决定的间隔进行插入排序,间隔会依次缩小,最后一次一定要是1。



// 打印元素
void PrintV(vector<int>& v){
    for(auto &e: v){
        cout << e << ' ';
    }
    cout << endl;
}


// 希尔排序
void ShellSort(vector<int>& v){
    int n = v.size();
    int h = 1;   // 间隔

    while(h < n / 3){
        h = 3 * h + 1;
    }

    while(h >= 1){
        for(int i=h; i < n; ++i){
            for(int j=i; j >= h && v[j] < v[j-h]; j-=h){
                swap(v[j], v[j-h]);
            }
        }
        h = h / 3;
    }
}



int main(){
    vector<int> v{7, 30, 16, 4, 1, 2, 9};
    cout << "希尔排序前: ";
    PrintV(v);
    ShellSort(v);
    cout << "希尔排序后: ";
    PrintV(v);
    return 0;
}

四、希尔排序时间复杂度总结:

  • 平均时间复杂度: O(nlogn)——O(n**2)
  • 最好情况:O(n**1.3)
  • 最坏情况:O(n**2)
  • 空间复杂度:O(1)
  • 稳定性:不稳定