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

简单排序


 

排序算法的模板函数

 

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 );

  }

}

 


 

猜你喜欢

转载自www.cnblogs.com/Davirain/p/11750143.html