• 排序算法 之 冒泡排序 插入排序 希尔排序 堆排序


    简单排序


     

    排序算法的模板函数

     

    void x_Sort( ElementType A[], int N)

      大多数情况下, 为简单起见,讨论都是从小到大的整数排序

     

    N 是 整数

    只讨论基于比较的排序(> = < 有定义)

    只讨论内部排序

    稳定性: 任意两个相等的数据,排序前后的相对位置不发生变化

     

    没有一种排序是任何情况下都表现最好的

     

    冒泡排序

     

    void Bubble_Sort ( ElementType A[], int N )

    {

      int flag;

      for ( int P = N - 1; P >= 0; P-- ){

        flag = 0;

        for ( int i = 0; i < P; i++ ) { //一趟冒泡

          if ( A[i] > A[i+1] ) {

            Swap ( A[i], A[i+1] );

            flag = 1;//标识发生了交换

          }

        }

        if ( flag == 0 ) break; //全称无交换

      }

    }

    最好情况: 顺序 T = O( N ) 

    最坏情况: 逆序 T = O( N^2 ) 

    冒泡排序算法是稳定的排序算法。

     

    插入排序

    /**********插入排序算法***********************/

    void Insertion_Sort ( ElementType A[], int N )

    {

      ElementType Tmp;

      for ( int P = 1; P < N; P++ ) {//默认起始时,只有一个元素时就是有序的

        Tmp = A[ P ];//摸下一张牌, 即取出未排序序列中的第一个元素

        for ( int i = P; i > 0 && A[ i - 1] > Tmp; i-- )

          A[ i ]  = A[ i - 1]; // 移出空位, 即一次与已经排序序列中元素比较并右移

        A[ i ]  = Tmp;//新牌落位, 即放进合适的位置

      }

    }

    最好情况 : 顺序 T = O( N )

    最坏情况 : 逆序 T = O ( N^2)

    插入排序是稳定的排序算法

     

    例: 给定初始序列 { 34, 8, 64, 51, 32, 21 },冒泡排序和插入排序

    分别需要多少次元素交换才能完成?

     

    时间复杂度下界

     

    对于下标 i < j , 如果 A [ i ] > A [ j ], 则称 (i , j)是一对逆序对(inversion)

    问题: 序列{ 34, 8, 64, 51, 32, 21}中有多少逆序对?

    (34, 8)  (34, 32) (34, 21) (64, 51) (64, 32) (64, 21) (51, 32) (51, 21) (32, 21)

    交换2个相邻元素正好消去一个逆序对!

    插入排序: T(N, I) = O( N + I)

      如果序列基本有序, 则插入排序简单且高效

     

    定理: 任意N个不同元素组成的序列平均具有 N (N - 1)/4个逆序对、

    定理 : 任何仅交换相邻两个元素来排序的算法, 其平均时间复杂度为 Ω(N^2)

    这意味着:要提高算法效率,我们必须 

      每次消去不止1个逆序对!

      每次交换相隔较远的两个元素!


     

    希尔排序 (by Donald Shell)

    定义增量序列 D_m > D_(m-1) > .... > D_1 = 1

    对每个D_k 进行"D_k - 间隔" 排序 (k = M, M -1, ..., 1)

    注意:“D_k间隔”有序的序列, 在执行“D_(k - 1) - 间隔”排序后, 仍然是

    "D_k - 间隔" 有序的

     

    希尔增量序列

    原始希尔排序 D_m = [N / 2]向下取整 , D_k = [ D_(k + 1) / 2] 向下取整

    void Shell_Sort ( ElementType A[], int N )

    {

      for ( int D = N / 2; D > 0; D /= 2) { //希尔增量序列

        for ( int P = D; P < N; P++) { // 插入排序

          Tmp = A[P];

          for ( int i = P; i >= D && A[i - D] > Tmp; i -= D )

            A[i] = A[i - D];

          A[i] = Tmp;

        }

      }

    最坏情况: T = Θ(N^2)

     

    更多的增量序列

     

     

    Sedgewick 增量序列

    void ShellSort ( ElementType A[], int N )

    {//希尔排序 -- 用Sedgewick 增量序列

      int Si, D, P, i;

      ElementType Tmp;

      //这里只列出了一小部分增量

      int Sedgewick[] = {929, 505, 209, 109, 41, 19, 5, 1, 0};

     

      for (Si = 0; Sedgewick[Si] >= N; Si++) 

        ;//初始的增量Sedgewick[Si] 不能超过待排序序列长度

      for ( D = Sedgewick[Si]; D > 0; D = Sedgewick[++Si] )

        for ( P = D; P < N; P++ ) { //插入排序

          Tmp = A[ P ];

          for ( i = P; i >= D && A[i - D] > Tmp; i -=D ) 

            A[i] = A[i - D];

          A[ i ]  = Tmp;

        }

    }


     

    堆排序

     

    选择排序

     

    void Selection_Sort ( ElementType A[], int N)

    {

      for ( int i = 0; i < N; i++ ) {

        MinPosition = ScanForMin ( A, i, N - 1 );

        // 从 A[i] 到A[N - 1] 中找到最小元, 并将其位置赋给MinPosition 

        Swap ( A[i], A[MinPosition] );

        // 将未排序的最小元换到有序部分的最后位置

      }

    }

    如何快速找到最小元????

     

    堆排序算法 

    算法1 

    void Heap_Sort ( ElementType A[], int N )

    {

      BuildHeap(A); // O(N)

      for ( int i = 0; i < N; i++ )

        TmpA[i] = DeleteMin(A); // O(logN)

      for ( int i = 0; i < N ; i++ ) // O(N)

        A[i] = TmpA[i];

    }

    T(N) = O( N logN )

    需要额外O(N) 空间, 并复制元素需要时间。

     算法2:

    void Heap_Sort ( ElementType A[], int N )

    {

      for ( int i = N/2 - 1; i >= 0; i-- )  // BuildHeap

        PerDown (A, i, N);

      for ( int i = N - 1; i > 0 ; i-- ) { 

        Swap( &A[0], &A[i] );     //DeleteMax 

        PerDown ( A, 0, i );

      }

    }

    定理: 堆排序处理 N 个不同元素的随机排列的平均比较次数是

      2 N logN - O( N log logN) .

    虽然堆排序给出最佳平均时间复杂度,但实际效果不如用Sedgewick增量序列的希尔排序。

     

    -- code --

    void Swap ( ElementType *a, ElementType *b)

    {

      ElementType t = *a;

      *a = *b;

      *b = t;

    }

    void PerDown( ElementType A[], int p, int N )

    {

      //将N个元素数组中以 A[p] 为根的子堆调整为最大堆

      int Parent, Child;

      ElementType X;

      

      X = A[p];//取出根结点存放的值

      for ( Parent  = p; (Parent*2 + 1) < N; ParentType = Child) {

        Child = Parent * 2 + 1;

        if ( (Child ! = N-1) && (A[Child] < A[Child + 1]) )

          Child++;//Child指向左右子节点的较大者

        if ( X >= A[Child] ) break;//找到合适位置

        else //下虑

          A[Parent] = A[Child];

      }

      A[Parent] = X;

    }

    void Heap_Sort ( ElementType A[], int N )

    {

      for ( int i = N/2 - 1; i >= 0; i-- )  // BuildHeap

        PerDown (A, i, N);

      for ( int i = N - 1; i > 0 ; i-- ) { 

        Swap( &A[0], &A[i] );     //DeleteMax 

        PerDown ( A, 0, i );

      }

    }

     


     

  • 相关阅读:
    Spring MVC学习03页面跳转
    Spring Boot学习07配置加载顺序
    Spring MVC学习01从空白Maven项目搭建环境
    Spring MVC学习05JSON序列化
    剑指Offer 44 数字序列中某一位的数字
    Spring MVC学习06异常解析器
    MSSQL·查看DB中所有表及列的相关信息
    MSSQL·查询数据库中所有索引的相关信息
    MSSQL·最长输出长度限制之解决方案
    .Net Core·热加载的实现及测试
  • 原文地址:https://www.cnblogs.com/Davirain/p/11750143.html
Copyright © 2020-2023  润新知