zoukankan      html  css  js  c++  java
  • 常用排序算法(1)

    常用排序算法(1) - 插入排序,希尔排序

    插入排序

    算法描述

    插入排序,顾名思义,就是将一个元素插入到一个有序队列里面,保持插入后队列依然保持有序。那么当我们拿到一个无序队列的时候,可以先将第一位看做是一个单元素的有序队列,然后把第二位插入这个有序队列中,随后这两个元素就变成了一个有序队列,然后重复同样步骤,依次往后,最终就可以得到该无序队列的排序。

    可以看出,插入排序算法对每个元素都需要与之前的元素进行对比,所以最坏情况下总共比较次数是 n * (n - 1) / 2,时间复杂度应该是O(n^2),但如果本来就是一个有序队列,每次都会在第一次比较时就已经可以停止,所以时间复杂度下限可以到达O(n)。

    因此,插入排序对近似有序的队列是十分高效的算法。

    实现

    //插入排序
    template <typename Comparable>
    void InsertionSort(vector<Comparable>& a)
    {
        for (int i = 1; i < a.size(); ++i)
        {
            auto temp = std::move(a[i]);
            int j = i;
            for (; j > 0 && a[j - 1] > tmp; --j)
            {
                a[j] = std::move(a[j - 1]);
            }
            a[j] = std::move(temp);
        }
    }
    

    后记

    给定一个数组 {8, 3, 4, 9, 1, 6}, 如果一个数比位置在其前面的数字大,可以称这两个数逆序,那么可以看出上面这个数组里面存在的逆序组有 [8, 3], [8, 4], [8, 1], [8, 6], [9, 1], [9, 6]。如果按照插入排序的做法,每次比较和交换相邻元素,最多可以消除一组逆序,因此可以引申得出,所有这种类型的排序算法(插入排序、冒泡排序、选择排序等),复杂度上限是无法突破O(n^2)的。所以,如果需要将复杂度再下降,排序算法需要做到单次操作能消除更多的逆序才行。下面描述的希尔排序就是根据插入排序的基本思想加上分组得来的。

    希尔排序

    算法描述

    希尔排序又被称为缩小增量排序,本质上是按照分组进行的插入排序,因为插入排序对于近似有序的队列时间复杂度较低的特性,通过分组按不同增量实现部分有序后,可以减少比较和交换次数,从而降低总体复杂度。

    称 h(n) 是以增量为n对序列分组,当 n=1 时 h(1) 就是直接插入排序。比较简单的增量取法是初始n取元素总数 x / 2 的值,每次将增量缩小一半直到为1。

    8   3   4   9   1   6   7   2
    A1  A2  A3  A4  B1  B2  B3  B4
    
    第一次排序后变为:
    1   3   4   2   8   6   7   9
    A1  A2  B1  B2  C1  C2  D1  D2
    
    第二次排序后:
    1   2   4   3   7   6   8   9
    A1  B1  C1  D1  E1  F1  G1  H1
    
    最后依次完成:
    1   2   3   4   6   7   8   9
    
    //希尔排序
    template <typename Comparable>
    void ShellSort(vector<Comparable>& a)
    {
        for (int gap = a.size() / 2; gap >= 0; gap /= 2)
        {
            //以gap为增量的插入排序
            for (int i = gap; i < a.size(); ++i)
            {
                auto tmp = std::move(a[i]);
                int j = i;
                for (; j >= gap && tmp < a[j - gap]; j -= gap)
                {
                    a[j] = std::move(a[j - gap]);
                }
                a[j] = std::move(tmp);
            }
        }
    }
    

    后记

    上面的增量选择是比较简单的做法,但增量的选择还有各种各样的算法,根据资料所说,比较高效的两种选择:

    • 由Sedgewick提出的(1, 5, 19, 41, 109,...)
    • 菲波拉契数列除去0和1后的剩余数列,以黄金分割的两倍的幂进行运算得到的数列(1, 9, 34, 182, 836, 4025, 19001, 90358, 428481, 2034035, 9651787, 45806244, 217378076, 1031612713,…)

    https://zh.wikipedia.org/wiki/希尔排序

  • 相关阅读:
    python操作Excel表格
    Spring的AntPathMatcher(路径匹配)
    【纪中受难记】——Day17:本来能AK
    PAT (Basic Level) Practice (中文)1009 说反话 (20 分)
    PAT (Basic Level) Practice (中文)1008 数组元素循环右移问题 (20 分)
    PAT (Basic Level) Practice (中文)C++ & python 语言实现 —— 题解目录
    PAT (Basic Level) Practice (中文)C++ & python 语言实现 —— 题解目录
    PAT (Basic Level) Practice (中文)1007 素数对猜想 (20 分)
    PAT (Basic Level) Practice (中文)1007 素数对猜想 (20 分)
    PAT (Basic Level) Practice (中文)1006 换个格式输出整数 (15 分)
  • 原文地址:https://www.cnblogs.com/zhqherm/p/12008673.html
Copyright © 2011-2022 走看看