• 希尔排序(Shell Sort)


    标签

    非稳定排序、原地排序、比较排序

    基本思想

     希尔排序是希尔(Donald Shell) 于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是直接插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破$O(n^2)$的第一批算法之一。它与插入排序的不同之处在于,它会优先比较距离较远的元素。

    希尔排序法会对相邻指定距离(称为增量)的元素进行比较,并不断把增量缩小至1,完成排序。排序开始时增量较大,分组较多,每组的记录数目较少,故在各组内采用直接插入排序较快,后来增量$t_i$逐渐缩小,分组数减少,各组的记录数增多,但由于已经按$t_{i−1}$分组排序,文件叫接近于有序状态,所以新的一趟排序过程较快。因此希尔排序在效率上比直接插入排序有较大的改进。

    算法描述

    我们选择增量$gap = length / 2$,缩小增量继续以$gap = gap / 2$的方式,这种增量选择我们可以用一个序列来表示:{n / 2, (n / 2) / 2, …, 1},称为增量序列。希尔排序的增量序列的选择与证明是个数学难题,我们选择的这个增量序列是比较常用的,也是希尔建议的增量,称为希尔增量,但其实这个增量序列不是最优的。
    先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:

    • 步骤1:选择一个增量序列$t_1, t_2, …, t_k$,其中$t_i > t_j, t_k = 1$;
    • 步骤2:按增量序列个数$k$,对序列进行$k$趟排序;
    • 步骤3:每趟排序,根据对应的增量$t_i$,将待排序列分割成若干长度为$m$的子序列,分别对各子表进行直接插入排序。仅增量因子为$1$时,整个序列作为一个表来处理,表长度即为整个序列的长度。

    动图演示

    时间复杂度

    希尔排序算法的时间复杂度分析比较复杂,实际所需的时间取决于各次排序时增量的个数和增量的取值。研究证明,若增量的取值比较合理,希尔排序算法的时间复杂度约为$O(n^{1.3})$。

    最好情况:$O(n^{1.3})$

    最坏情况:$O(nlog n)$

    平均情况:$O(nlog n)$

    对于增量的选择,Shell 最初建议增量选择为$n / 2$,并且对增量取半直到$1$;

    推荐序列

    Hibbard增量序列

    Hibbard增量序列的取法为$D_k = 2^k - 1: {1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191...}$。

    最坏时间复杂度为$O(N^{3/2})$;平均时间复杂度为$O(N^{5/4})$。

    序列代码(C++)

    // Hibbard增量序列
    // D(i)=2^i−1, i>0
    void getHibbardStepArr(int n, vector<int> &v) {
        int i = 1;
        while (1) {
            int tmp = (1 << i) - 1;
            if (tmp <= n) {
                v.push_back(tmp);
            }
            else {
                break;
            }
            i++;
        }
        return;
    }

    Sedgewick增量序列

    Sedgewick增量序列的取法为$D = 9 * 4^i - 9 * 2^i + 1$或$4^i - 3 * 2^i + 1: {1, 5, 19, 41, 109, 209, 505, 929, 2161...}$。

    最坏时间复杂度为$O(N^{4/3})$;平均时间复杂度为$O(N{7/6})$ 。

    序列代码(C++)

    // Sedgewick增量序列
    // D=9*4^i-9*2^i+1 或 4^(i+2)-3*2^(i+2)+1 , i>=0
    // 稍微变一下形:D=9*(2^(2i)-2^i)+1 或 2^(2i+4)-3*2^(i+2)+1 , i>=0
    void getSedgewickStepArr(int n, vector<int> &v):
        int i = 0;
        while (1) {
            int tmp = 9 * ((1 << 2 * i) - (1 << i)) + 1;
            if (tmp <= n) {
                arr.append(tmp)
            }
            tmp = (1 << 2 * i + 4) - 3 * (1 << i + 2) + 1;
            if (tmp <= n) {
                v.push_back(temp);
            }
            else {
                break;
            }
            i++;
        }
        return;
    }

    空间复杂度

    没有额外的空间开销。

    算法示例

    参考资料:

    https://blog.csdn.net/coolwriter/article/details/78732728

    https://blog.csdn.net/weixin_41190227/article/details/86600821

    https://www.cnblogs.com/itsharehome/p/11058010.html

    https://www.cnblogs.com/minxiang-luo/p/12392634.html

    Min是清明的茗
  • 相关阅读:
    spring管理hibernate,mybatis,一级缓存失效原因
    The constructor ClassPathXmlApplicationContext(String) refers to the missing type BeansException
    idea中Hibernate错误:无法解析表
    使用Dom4解析xml
    关于idea中新建web项目 webapp文件夹没有小蓝点 ,启动服务,访问不到解决方案
    解决VS编译太慢问题
    Entity FrameWork6 Code First带virtual关键字外键 Asp.Net WebApi无法返回实体类数据问题
    webapi请求返回{"$id":"1","Message":"请求的资源不支持 http 方法“GET”。"}
    WPF的BusyIndicator控件只显示遮罩层,不显示提示层问题
    [转]Mysql将数据分组后取出时间最近的数据
  • 原文地址:https://www.cnblogs.com/MinPage/p/13963630.html
Copyright © 2020-2023  润新知