• 排序算法:插入排序


    1. 直接插入排序

    直接插入排序是一种最简单的排序方法,基本操作是将一条记录插入到已排好序的有序表中,从而得到一个新的、记录数量增1的有序表。
    直接插入排序过程:
    2
    算法描述:

    void insertSort(SqList &L) {
      for(int i=2; i<=L.length; i++) {
        if(L[i] < L[i-1]) {             // 需将L[i]插入有序子表
          L[0] = L[i];                  // 将待插入的记录暂存在监视哨中
          L[i] = L[i-1];                // r[i-1]后移
          int j;
          for(j=i-2; L[j] > L[0]; j--){ // 注意循环停止的条件是 第一个 L[j] > L[0]的时候, 该点及之前的全都不再向后移,L[0]放在L[j+1]的位置
             L[j+1] = L[j];
          }
          L[j+1] = L[0];
        }
      }
    }
    

    算法分析:
    时间复杂度 (O(n^2))
    空间复杂度(O(1)) (只需一个监视哨)

    算法特点:
    (1)稳定排序
    (2)也适用于链表结构
    (3)更适用于初始记录基本有序(正序)的情况,当初始记录无序,n较大时,此算法复杂度较高,不宜采用

    2. 折半插入排序

    直接插入排序采用 顺序查找法查找当前记录在已排好序的序列中的插入位置,由查找算法可知,这个过程可采用“折半查找”来实现,由此进行的插入排序成为 折半插入排序。
    算法描述:

    void BInsertSort(SqList &L) {
      for(int i=2; i<=L.length; i++) {
        L[0] = L[i];
        int low = 1, high = i-1;
        while(low <= high) {
          int mid = (low + high) / 2;
          if(L[0] < mid) high = mid - 1;
          else low = mid + 1;            // 等于也包含在内(稳定排序);直接放着就可以,后面不用再对这一部分做处理,只处理high的部分
        }                                // 注意之后的代码中如何利用low与high!
        for(j = i-1; j >= high+1; j--) { // 只用到了high,high后面的全部后移一格
          L[j+1] = L[j];
        }
        L[high+1] = L[0]; // 应用high
      }
    }
    

    算法分析:
    时间复杂度 (O(n^2)) (只减少了比较次数,记录的移动次数不变)
    空间复杂度(O(1)) (只需一个监视哨)

    算法特点:
    (1)稳定排序
    (2)因为进行了折半查找,所以只能用于顺序结构,不能用于链式结构
    (3)适合初始记录无序,n较大的情况(why?)

    3. 希尔排序

    直接插入排序,当待排序序列个数较少,且待排序序列基本有序时,效率较高。
    希尔排序基于以上两点,从“减少记录个数”和“序列基本有序”两方面对直接插入排序进行了改进。

    希尔排序实质是分组插入的方法。其分组不是简单的“逐块分组”,二是将相隔某个“ 增量”的记录分成一组。

    2

    如图:
    (1)第一趟取增量(d_1 = 5),所有间隔为5的记录分为1组,共5组,各组中分别进行直接插入排序。
    (2)第二趟取增量(d_2 = 3),所有间隔为3的记录分为1组,共3组,各组中分别进行直接插入排序。
    (3)第三趟取增量(d_3 = 1),对整个序列进行一趟直接插入排序,排序完成。

    算法描述:
    可通过改写直接插入排序,改写部分主要有:
    (1)前后记录位置的增量是dk,而不是1;
    (2)r[0]只是暂存单元,不是哨兵。当j<=0时,插入位置已找到。(?)

    void shellSort(SqList &L, int dt[], int t) {
      for (k=0; k<t; k++) {
        shellInsert(L, dt[k]);
      }
    
    }
    
    void shellInsert(SqList &L, int dk) {
      for(int i=dk+1; i<=L.length; i++) {
        if(L[i] < L[i-dk]) {             // 需将L[i]插入有序子表
          L[0] = L[i];                  // 暂存在L[0]中,但不是监视哨
          L[i] = L[i-1];                // r[i-1]后移
          int j;
          for(j=i-dk; j>0 && L[0]<L[j]; j-=dk){ // 直接插入排序,可走到监视哨,出现相等,自然不满足L[0]<L[j]条件,但有增量后无法这样判断退出,因此需限定j>0
             L[j+dk] = L[j];
          }
          L[j+dk] = L[0];
        }
      }
    }
    

    算法分析:
    时间复杂度:低于直接插入排序
    空间复杂度:O(1)

    算法特点:
    (1)记录跳跃式移动导致排序方法是不稳定的
    (2)只能用于顺序结构不能用于链式结构
    (3)应使增量序列中的值没有除1之外的公因子,且最后一个增量值必须等于1
    (4)记录总的比较次数和移动次数都比直接插入排序少,n越大,越明显。所以适合初始记录无序、n较大的情况

    步履不停
  • 相关阅读:
    pycharm 安装第三方库报错:AttributeError: 'module' object has no attribute 'main'
    工作冥想
    对于测试工作与测试人员未来出路的思考
    测试计划再谈
    python 反转列表的3种方式
    关于最近练习PYTHON代码的一点心得
    python sum()函数的用法
    python count()函数
    SpringCloud和SpringBoot的详细版本说明
    使用 lntelliJ IDEA 创建 Maven 工程的springboot项目
  • 原文地址:https://www.cnblogs.com/yuanyunjing/p/14930711.html
Copyright © 2020-2023  润新知