排序算法 冒泡+插入+希尔+选择+堆排序+归并+快排

冒泡排序

特点:稳定排序
优点:可用于链表排序,数组排序
复杂度:顺序最好O(N),逆序最坏O(N^2)

从序列头部遍历到尾部,将相邻的前者必后者大的两元素交换;

void Swap(int &a,int &b)
{
	int temp=a;
	a=b;
	b=temp;
}

void BubbleSort()
{
	int i=N-1;
	int flag;
	while(i){
		flag=1;
		for(int j=0;j<i;j++)
			if(T[j]>T[j+1]){
				Swap(T[j],T[j+1]);	
				flag=0;
			}
			if(flag) break;
			--i;
		}	
}

插入排序

特点:稳定排序
复杂度:顺序最好O(N),逆序最坏O(N^2)

取要排序的元素放tmp,与已排序好的序列从后往前比较,若小于序列中元素,则序列中元素后移,直到碰到大于tmp的,插入其后即可;

void InsertionSort( ElementType A[], int N )
{ /* 插入排序 */
     int P, i;
     ElementType Tmp;
      
     for ( P=1; P<N; P++ ) {
         Tmp = A[P]; /* 取出未排序序列中的第一个元素*/
         for ( i=P; i>0 && A[i-1]>Tmp; i-- )
             A[i] = A[i-1]; /*依次与已排序序列中元素比较并右移*/
         A[i] = Tmp; /* 放进合适的位置 */
     }
}

概念:逆序对

每次相邻数字的交换,可以消除一个逆序对;
故冒泡排序和插入排序的交换次数为逆序对次数;
要减少交换次数,就需要让每次交换消灭更多的逆序对;

希尔排序

特点:不稳定排序
定义一个递增序列Dk;Dk>Dk-1>Dk-2……>Dk1=1;
每隔Dk个元素进行插入排序;
增量元素不互质,小增量可能不起作用
增量的选取很关键:
Hibbard增量序列:Dk=2^k-1——相邻元素互质
Sedgewick增量序列:{1, 5, 19, 41, 109, … } 猜想:Tavg = O ( N7/6 ),Tworst = O ( N4/3 )

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

最坏情况: T = O( N^2 )
每次交换可能可以消灭多个逆序对;

选择排序

特点:不稳定排序
每次找到未排序序列中的最小元,交换放入有序序列最后位置,与该位置原本元素交换;
最坏情况:N次交换;
关键在于 找到每次未排序序列中的最小元,若用顺序查找,需要O(N)的复杂度;
总复杂度为O(N^2)

堆排序

特点:不稳定排序
在选择排序的基础上,用堆来查找每次的最小元;
若直接查找最小元,则需要新建空间来储存堆和结果;
不浪费空间
创建最大堆,每次把堆头和堆尾交换,再忽略堆尾的最大元素,重新调整前面的堆为最大堆——循环N次即把N个数字都已排序在堆数组中;
堆排序:可以快速找到特定数量的最大元素或最小元素;
堆排序从0开始记,第i个元素的左子元素为(i+1) * 2-1,右子元素为(i+1) * 2;
复杂度
堆排序的平均复杂度O(NlogN),略小于NlogN;
但实际效果可能不如希尔排序配合Sedgewick增量;

void Swap( ElementType *a, ElementType *b )  
{
     ElementType t = *a; *a = *b; *b = t;
}
  
void PercDown( ElementType A[], int p, int N )
{ /* 改编代码4.24的PercDown( MaxHeap H, int p )    */
  /* 将N个元素的数组中以A[p]为根的子堆调整为最大堆 */
    int Parent, Child;
    ElementType X;
 
    X = A[p]; /* 取出根结点存放的值 */
    for( Parent=p; (Parent*2+1)<N; Parent=Child ) {
        Child = Parent * 2 + 1;
        if( (Child!=N-1) && (A[Child]<A[Child+1]) )
            Child++;  /* Child指向左右子结点的较大者 */
        if( X >= A[Child] ) break; /* 找到了合适位置 */
        else  /* 下滤X */
            A[Parent] = A[Child];
    }
    A[Parent] = X;
}
 
void HeapSort( ElementType A[], int N ) 
{ /* 堆排序 */
     int i;
       
     for ( i=N/2-1; i>=0; i-- )/* 建立最大堆 */
         PercDown( A, i, N );
      
     for ( i=N-1; i>0; i-- ) {
         /* 删除最大堆顶 */
         Swap( &A[0], &A[i] ); /* 见代码7.1 */
         PercDown( A, 0, i );
     }
}

归并排序

特点:稳定排序


需要额外内存空间,适合外部排序
归并:将已排序子序列按顺序合并至另外一个序列空间中;时间复杂度为O(N)
根据二分法:一共要logN次归并;

递归实现归并排序(需要堆栈,空间占用更多)
注意点

void Msort( ElementType A[], ElementType TmpA[], int L, int RightEnd )
{ /* 核心递归排序函数 */ 
     int Center;
      
     if ( L < RightEnd ) {
          Center = (L+RightEnd) / 2;
          Msort( A, TmpA, L, Center );              /* 递归解决左边 */ 
          Msort( A, TmpA, Center+1, RightEnd );     /* 递归解决右边 */  
          Merge( A, TmpA, L, Center+1, RightEnd );  /* 合并两段有序序列 */ 
     }
}

这里Msort一定要时center+1,Rightend;
若使用L,center-1; center+1,Rightend的话,
Msort(N,N+1)会调用Msort(N,N-1)和Msort(N-1,N+1),又会重新调用Msort(N,N+1),即无限循环;

/* 归并排序 - 递归实现 */
 
/* L = 左边起始位置, R = 右边起始位置, RightEnd = 右边终点位置*/
void Merge( ElementType A[], ElementType TmpA[], int L, int R, int RightEnd )
{ /* 将有序的A[L]~A[R-1]和A[R]~A[RightEnd]归并成一个有序序列 */
     int LeftEnd, NumElements, Tmp;
     int i;
      
     LeftEnd = R - 1; /* 左边终点位置 */
     Tmp = L;         /* 有序序列的起始位置 */
     NumElements = RightEnd - L + 1;
      
     while( L <= LeftEnd && R <= RightEnd ) {
         if ( A[L] <= A[R] )
             TmpA[Tmp++] = A[L++]; /* 将左边元素复制到TmpA */
         else
             TmpA[Tmp++] = A[R++]; /* 将右边元素复制到TmpA */
     }
 
     while( L <= LeftEnd )
         TmpA[Tmp++] = A[L++]; /* 直接复制左边剩下的 */
     while( R <= RightEnd )
         TmpA[Tmp++] = A[R++]; /* 直接复制右边剩下的 */
          
     for( i = 0; i < NumElements; i++, RightEnd -- )
         A[RightEnd] = TmpA[RightEnd]; /* 将有序的TmpA[]复制回A[] */
}
 
void Msort( ElementType A[], ElementType TmpA[], int L, int RightEnd )
{ /* 核心递归排序函数 */ 
     int Center;
      
     if ( L < RightEnd ) {
          Center = (L+RightEnd) / 2;
          Msort( A, TmpA, L, Center );              /* 递归解决左边 */ 
          Msort( A, TmpA, Center+1, RightEnd );     /* 递归解决右边 */  
          Merge( A, TmpA, L, Center+1, RightEnd );  /* 合并两段有序序列 */ 
     }
}
 
void MergeSort( ElementType A[], int N )
{ /* 归并排序 */
     ElementType *TmpA;
     TmpA = (ElementType *)malloc(N*sizeof(ElementType));
      
     if ( TmpA != NULL ) {
          Msort( A, TmpA, 0, N-1 );
          free( TmpA );
     }
     else printf( "空间不足" );
}

非递归实现归并排序(更快)
注意点

for ( i=0; i <= N-2*length; i += 2*length )           //处理成对部分

先处理N-2*length前面的i,若正好为偶数个,则还剩两个子列;

if ( i+length < N ) /* 归并最后2个子列*/

最后一个个子列长度不定,故归并方式为

 Merge( A, TmpA, i, i+length, N-1);

该函数为非递归的归并函数重点;

若只剩一个子列,则无需归并操作;

/* 归并排序 - 循环实现 */
/* 这里Merge函数在递归版本中给出 */
 
/* length = 当前有序子列的长度*/
void Merge_pass( ElementType A[], ElementType TmpA[], int N, int length )
{ /* 两两归并相邻有序子列 */
     int i, j;
       
     for ( i=0; i <= N-2*length; i += 2*length )           //处理成对部分
         Merge( A, TmpA, i, i+length, i+2*length-1 );
     
//尾巴另作处理
	if ( i+length < N ) /* 归并最后2个子列*/
         Merge( A, TmpA, i, i+length, N-1);
     else /* 最后只剩1个子列*/
         for ( j = i; j < N; j++ ) TmpA[j] = A[j];
}
 
void Merge_Sort( ElementType A[], int N )
{ 
     int length; 
     ElementType *TmpA;
      
     length = 1; /* 初始化子序列长度*/
     TmpA = malloc( N * sizeof( ElementType ) );
     if ( TmpA != NULL ) {
          while( length < N ) {      //length=N表示N个数字都已经归并成功
              Merge_pass( A, TmpA, N, length );
              length *= 2;
              Merge_pass( TmpA, A, N, length );
              length *= 2;
          }
          free( TmpA );
     }
     else printf( "空间不足" );
}

快速排序

快速排序库函数

/* 快速排序 - 直接调用库函数 */
 
#include <stdlib.h>
 
/*---------------简单整数排序--------------------*/
int compare(const void *a, const void *b)
{ /* 比较两整数。非降序排列 */
    return (*(int*)a - *(int*)b);
}
/* 调用接口 */ 
qsort(A, N, sizeof(int), compare);
/*---------------简单整数排序--------------------*/
 
 
/*--------------- 一般情况下,对结构体Node中的某键值key排序 ---------------*/
struct Node {
    int key1, key2;
} A[MAXN];
  
int compare2keys(const void *a, const void *b)
{ /* 比较两种键值:按key1非升序排列;如果key1相等,则按key2非降序排列 */
    int k;
    if ( ((const struct Node*)a)->key1 < ((const struct Node*)b)->key1 )
        k = 1;
    else if ( ((const struct Node*)a)->key1 > ((const struct Node*)b)->key1 )
        k = -1;
    else { /* 如果key1相等 */
        if ( ((const struct Node*)a)->key2 < ((const struct Node*)b)->key2 )
            k = -1;
        else
            k = 1;
    }
    return k;
}
/* 调用接口 */ 
qsort(A, N, sizeof(struct Node), compare2keys);
/*--------------- 一般情况下,对结构体Node中的某键值key排序 ---------------*/

三数中位数快速排序
若待排序的数少于cutoff,则使用插入排序等简单潘旭更快;
若待排序的数很大,则使用快速排序更快;

/* 快速排序 */
 
ElementType Median3( ElementType A[], int Left, int Right )
{ 
    int Center = (Left+Right) / 2;
    if ( A[Left] > A[Center] )
        Swap( &A[Left], &A[Center] );
    if ( A[Left] > A[Right] )
        Swap( &A[Left], &A[Right] );
    if ( A[Center] > A[Right] )
        Swap( &A[Center], &A[Right] );
    /* 此时A[Left] <= A[Center] <= A[Right] */
    Swap( &A[Center], &A[Right-1] ); /* 将基准Pivot藏到右边*/
    /* 只需要考虑A[Left+1] … A[Right-2] */
    return  A[Right-1];  /* 返回基准Pivot */
}
 
void Qsort( ElementType A[], int Left, int Right )
{ /* 核心递归函数 */ 
     int Pivot, Cutoff, Low, High;
       
     if ( Cutoff <= Right-Left ) { /* 如果序列元素充分多,进入快排 */
          Pivot = Median3( A, Left, Right ); /* 选基准 */ 
          Low = Left; High = Right-1;
          while (1) { /*将序列中比基准小的移到基准左边,大的移到右边*/
               while ( A[++Low] < Pivot ) ;
               while ( A[--High] > Pivot ) ;
               if ( Low < High ) Swap( &A[Low], &A[High] );
               else break;
          }
          Swap( &A[Low], &A[Right-1] );   /* 将基准换到正确的位置 */ 
          Qsort( A, Left, Low-1 );    /* 递归解决左边 */ 
          Qsort( A, Low+1, Right );   /* 递归解决右边 */  
     }
     else InsertionSort( A+Left, Right-Left+1 ); /* 元素太少,用简单排序 */ 
}
 
void QuickSort( ElementType A[], int N )
{ /* 统一接口 */
     Qsort( A, 0, N-1 );
}
发布了105 篇原创文章 · 获赞 6 · 访问量 4947

猜你喜欢

转载自blog.csdn.net/BLUEsang/article/details/105531922