以下算法默认以排成从小到大形式
冒泡排序 O(n^2)
冒泡排序就是遍历一次把最大的放到最后面,下一次排序把第二大的放到倒数第二个位置,直到第一个位置,即从小到大排序。
void Bubble_Sort(ElementType A[] , int n)
{
for (int i=n-1;i>=0;i--)
{
int flag = 0 ;
for (int j=0;j<i;j++)
{
if (A[j]>A[j+1])
{
Swap(A[j],A[j+1]);
flag = 1; //记录是否发生交换
}
}
if (flag==0) break ; //未发生交换说明有序,结束排序
}
}
插入排序 O(n^2)
插入排序就像城里扑克牌一样,一张张的插入,没插入一张,从后往前推,如果比插入的大则往后退一个位置,直到找到比插入的数小的位置时,放入即可。
void Insertion_Sort(ElementType A[] , int n)
{
int p , i;
for (i=1;i< n;i++)
{
int item = A[i];
for ( p = i ; p>0 && A[p-1]>item ;p--)
A[p] = A[p-1];
A[p] = item ;
}
}
希尔排序 O(n ^(4/3))
定义一串间隔d ,d从大到小,分别对每个间隔为di 的元素进行插入排序。Sedgewick增量序列是效率最高的。
int Sedgewick[] = {0,1, 5 ,19, 41, 109, 209, 505, 929, 2161, 3905 ,8929, 16001, 36289, 64769, 146305, 260609 ,587521, 1045505, 2354689 ,4188161 } ;
排序代码:
void Shell_sort(ElementType A[], int n)
{
int Sedgewick[] = {0,1, 5 ,19, 41, 109, 209, 505, 929, 2161, 3905 ,8929, 16001, 36289, 64769, 146305, 260609 ,587521, 1045505, 2354689 ,4188161 } ;
int si ,d,i,j,tmp;
for (si = 0 ;Sedgewick[si]<=n;si++ );//间隔不能超过序列个数
for ( d = Sedgewick[si] ; d>0 ; d=Sedgewick[--si] ) //间隔逐渐缩小到1
{
for ( i= d ; i<n ;i++)
{
tmp = A[i];
for ( j=i ;j>=d && A[j-d] >tmp;j-=d )
A[j] = A[j-d];
A[j] = tmp;
}
}
}
堆排序 O(nlogn)
依赖堆删除的方法,每次把堆顶的元素删除,放到数组末尾,然后再调整堆顶,这样重复n次数字就成从小到大排序了。
建堆的地方有一个小细节要注意,之前博客介绍最大堆时第一个下标0的元素是哨兵的,所以PercDown的时候是调整到1的位置,排序的时候没有哨兵因此是到0才是堆顶的结点。
堆讲解:https://blog.youkuaiyun.com/qq_20225851/article/details/104148777
void PercDown(ElementType A[] , int p , int n) //大堆 调整p为堆顶的堆结构
{
int item = A[p];
int parent , child ;
for (parent = p ; parent*2<=n;parent = child )
{
child = parent*2;
if (child <n && A[child]<A[child+1])
child ++ ;
if (item> A[child]) break ;
else
A[parent] = A[child];
}
A[parent] = item ;
}
void Heap_sort(ElementType A[] , int n)
{
n--; //数组下标只有到n-1
for (int i=n/2 ;i>=0;i--)
PercDown (A, i,n); //调整堆
for (int i=n-1 ;i>0;i--)
{
Swap(A[0],A[i+1]); //堆顶元素与数组最后一个元素呼唤, 当前最后一个元素的数组下标是i+1
PercDown(A,0,i);
}
}
归并排序 O(nlogn) 稳定
归并排序是利用二分的思想分为两份,分别排序,然后再合并,每以份又可以递归的分成两份。这种排序,不依赖序列的逆序数,是比较稳定的排序方法。
void merge(ElementType A[] , ElementType B[],int L, int R ,int RightEnd)
{
int LeftEnd = R-1 ;
int flag = L ;//初始位置
int first = L ;
int last = RightEnd;
while (L<=LeftEnd && R<= RightEnd)
{
if (A[L] > A[R])
B[flag++] = A[R++];
else
B[flag++] = A[L++];
}
while (L<=LeftEnd) B[flag ++] = A[L++];
while (R<=RightEnd) B[flag++] = A[R++];
for (int i = first ; i<=last ;i++)
A[i] = B[i];
}
void Msort(ElementType A[], ElementType B[] ,int first ,int last)
{
if (first < last)
{
int center = (first +last) / 2;
Msort(A, B , first ,center);
Msort(A,B,center+1 , last);
merge(A,B,first,center+1 , last);
}
}
void Merge_sort(ElementType A[], int n)
{
ElementType *B ;
B = (ElementType *)malloc(sizeof(ElementType)*n);
Msort (A,B,0,n-1);
free (B);
}
测试代码:
#include <iostream>
#include <stdlib.h>
#include <time.h>
#define ElementType int
#define Max 100000
using namespace std ;
void Swap(int &a, int &b)
{
int item = a ;
a= b ;
b =item ;
}
void Bubble_Sort(ElementType A[] , int n)
{
for (int i=n-1;i>=0;i--)
{
int flag = 0 ;
for (int j=0;j<i;j++)
{
if (A[j]>A[j+1])
{
Swap(A[j],A[j+1]);
flag = 1; //记录是否发生交换
}
}
if (flag==0) break ; //未发生交换说明有序,结束排序
}
}
void Insertion_Sort(ElementType A[] , int n)
{
int p , i;
for (i=1;i< n;i++)
{
int item = A[i];
for ( p = i ; p>0 && A[p-1]>item ;p--)
A[p] = A[p-1];
A[p] = item ;
}
}
void print(ElementType A[] , int n)
{
cout <<"排序结果:";
for (int i=0;i<n;i++)
cout << A[i]<<" ";
}
void Shell_sort(ElementType A[], int n)
{
int Sedgewick[] = {0,1, 5 ,19, 41, 109, 209, 505, 929, 2161, 3905 ,8929, 16001, 36289, 64769, 146305, 260609 ,587521, 1045505, 2354689 ,4188161 } ;
int si ,d,i,j,tmp;
for (si = 0 ;Sedgewick[si]<=n;si++ );
for ( d = Sedgewick[si] ; d>0 ; d=Sedgewick[--si] )
{
for ( i= d ; i<n ;i++)
{
tmp = A[i];
for ( j=i ;j>=d && A[j-d] >tmp;j-=d )
A[j] = A[j-d];
A[j] = tmp;
}
}
}
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 );
}
}
void merge(ElementType A[] , ElementType B[],int L, int R ,int RightEnd)
{
int LeftEnd = R-1 ;
int flag = L ;//初始位置
int first = L ;
int last = RightEnd;
while (L<=LeftEnd && R<= RightEnd)
{
if (A[L] > A[R])
B[flag++] = A[R++];
else
B[flag++] = A[L++];
}
while (L<=LeftEnd) B[flag ++] = A[L++];
while (R<=RightEnd) B[flag++] = A[R++];
for (int i = first ; i<=last ;i++)
A[i] = B[i];
}
void Msort(ElementType A[], ElementType B[] ,int first ,int last)
{
if (first < last)
{
int center = (first +last) / 2;
Msort(A, B , first ,center);
Msort(A,B,center+1 , last);
merge(A,B,first,center+1 , last);
}
}
void Merge_sort(ElementType A[], int n)
{
ElementType *B ;
B = (ElementType *)malloc(sizeof(ElementType)*n);
Msort (A,B,0,n-1);
free (B);
}
int main ()
{
clock_t start ,stop ;
double T ;
int A[Max] ,B[Max];
for (int i=0 ; i< Max; i++)
A[i] = rand();
for (int i=0 ; i< Max; i++)
B[i] = A[i];
start = clock ();
Bubble_Sort(B , Max);
stop = clock();
T = (double)(stop-start)/CLOCKS_PER_SEC;
cout <<"冒泡排序用时:"<<T<<"s"<<endl;
for (int i=0 ; i< Max; i++)
B[i] = A[i];
start = clock ();
Insertion_Sort(B , Max);
stop = clock();
T = (double)(stop-start)/CLOCKS_PER_SEC;
cout <<"插入排序用时:"<<T<<"s"<<endl;
for (int i=0 ; i< Max; i++)
B[i] = A[i];
start = clock ();
Shell_sort(B , Max);
stop = clock();
T = (double)(stop-start)/CLOCKS_PER_SEC;
cout <<"希尔排序用时:"<<T<<"s"<<endl;
for (int i=0 ; i< Max; i++)
B[i] = A[i];
start = clock ();
HeapSort(B , Max);
stop = clock();
T = (double)(stop-start)/CLOCKS_PER_SEC;
cout <<"堆排序用时:"<<T<<"s"<<endl;
for (int i=0 ; i< Max; i++)
B[i] = A[i];
start = clock ();
Merge_sort(B , Max);
stop = clock();
T = (double)(stop-start)/CLOCKS_PER_SEC;
cout <<"归并排序用时:"<<T<<"s"<<endl;
}
这是100000个数据每个排序算法的时间对比
通过对比可以看到,最后三种排序的效率是差不多的,上面五中排序算法,归并排序是最稳定的。
插入会比冒泡快那么多是因为随机生成的数据更有序化,所以会比冒泡快。
程序竞赛时,如果数据范围达到5次方,限时一秒时是不能用O(n^2)的排序算法的,会直接超时因为需要30s左右 的时间,其他算法也是一样的,要设计出nlogn复杂度一下的算法才有可能AC。