关于排序算法的性能和稳定性总结,维基百科中文词条排序算法的总结很全面。
本文统一将数组从小到大排序。
1.插入排序
(1)直接插入排序,基本操作是将一个记录插入到已经排好序的的有序表中,从而得到一个新的,记录数曾1的有序表。时间复杂度O(n2)。
void InsertSort(int a [], int size){ int i,j,temp; for(i = 1; i < size; i++){ if(a[i] < a[i-1]){ temp = a[i]; for(j = i-1; temp < a[j] && j >=0; j--){ a[j+1] = a[j]; } a[j+1] = temp; } } }
(2)折半插入排序。时间复杂度O(n2)。
void BInsertSort(int a[],int length) { int i,j,temp,low,high,middle; for(i = 1; i < length; i++) { temp = a[i]; low = 0; high = i -1; while( low <= high) { middle = (low + high) / 2; if(temp < a[middle])high = middle -1; else low = middle + 1; } for(j = i; j > high; j--)a[j] = a[j-1]; a[high+1] = temp; } }
(3)希尔排序。
#include <stdio.h> void ShellInsert(int a[], int length, int d){ int i,j; int temp; for(i = d; i < length; i+=d){ if(a[i-d] > a[i]){ temp = a[i]; for(j = i -d; j >=0 && temp < a[j]; j-=d) a[j + d] = a[j]; a[j+d] = temp; } } } void ShellSort(int a[],int length, int delt[], int dsize){ int i; for(i = 0; i < dsize; i++){ ShellInsert(a, length, delt[i]); } } int main(int argc, char** argv) { int a[10] = {0,1,5,8,10,6,20,100,-1,-2}; int delt[3] = {3,2,1}; ShellSort(a,10,delt,3); for (int i = 0; i <= 9; i++){ printf("%d ", a[i]); } return 0; }
2.交换排序
(1)冒泡排序。时间复杂度O(n2)。
void bubbleSort(int a[], int length){ //0号元素用上 int i, j,temp ; for(i = 0; i < length - 1; i++){ for(j = 0; j < length - i - 1; j++){ if(a[j] > a[j+1]){ temp = a[j]; a[j] = a[j + 1]; a[j + 1] = temp; } } } }
(2)快速排序。快速排序是冒泡排序的一种改进。它的基本思想是,通过一趟排序将排序记录分割成独立的两部分,其中一部分的关键字均比另一部分的关键字小,中间的枢轴点移动到它最后的位置。然后分别对左右两部分的记录继续排序,以达到整个序列有序。时间复杂度平均为O(nlog(n)),最坏情况(与结果完全反序)为O(n2)。
int Partition(int a[], int low, int high){ int pivotkey = a[low]; while(low < high){ while( low < high && a[high] >= pivotkey) --high; a[low]= a[high]; while(low < high && a[low] <= pivotkey) ++low; a[high] = a[low]; } a[low] = pivotkey; return low; } void Qsort(int a[], int low, int high){ int pivotloc; if(low < high){ pivotloc = Partition(a, low, high); Qsort(a, low, pivotloc - 1); Qsort(a, pivotloc + 1, high); } } void QuikSort(int a[], int size){ Qsort(a, 0 ,size-1); }
3.选择排序
(1)简单选择排序。
(2)堆排序。时间复杂度为O(nlog(n))。
/* * File: main.cpp * Author: brownwn * * Created on 2015年1月14日, 下午3:07 */ #include <cstdlib> #include <stdio.h> void HeapAdjust(int a[], int s, int m){ int rc = a[s]; int j; for(j=2*s;j<=m;j*=2){ if(j<m && a[j] < a[j+1]) ++j; if(rc >= a[j]) break; a[s] = a[j]; s = j; } a[s] = rc; } //int a[] 1-length,a[0] not use void heapSort(int a[], int length){ int i; for( i = length/2; i>0; --i){ //调整为大顶堆 HeapAdjust(a,i,length); } //此时a[1]最大 for(i = length; i > 1; --i){ int temp = a[1]; a[1] = a[i]; a[i] = temp; HeapAdjust(a,1,i-1); } // return a[1]; 可以返回第一个值,最小值。 } int main(int argc, char** argv) { int a[7] = {0,1,5,8,10,6,20}; //注意这个数组,给索引1-7排序,所以长度为6。索引0的元素是没有用的。 heapSort(a, 6); for (int i = 1; i <= 6; i++){ printf("%d ", a[i]); } return 0; }
4.归并排序。时间复杂度为O(nlog(n))。
以2-路归并算法为例来说明归并算法的过程:
初始关键字:(49) (38) (65) (97) (76) (13) (27)
一趟归并后:(38 49) (65 97) (13 76) (27)
二趟归并后:(38 49 65 97) (13 27 76)
三趟归并后:(13 27 38 49 65 76 97)
2-路归并的核心操作是将一维数组中前后相邻的两个有序序列归并为一个有序序列,算法描述如下:
void Merge(int a[], int &b[], int i, int m, int n){ //将有序的a[i....m]和a[i+1....n]归并为有序的b[i....n] for(j = m+1,k=i; i<=m&&j<=n; ++k){ if(a[i] < [j])b[k] = a[i++]; else b[k] = a[j++]; } if(i <= m)b[k....n] = a[i...m]; if(j <= n)b[k....n] = a[j...n]; }
归并算法最大的特点就是它是稳定排序。
5.基数排序
基数排序是一种借助多关键字排序的思想对单逻辑关键字进行排序的方法。基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digital)。比如LSD的排序 流程如下:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
81 | 22 | 73 93 43 | 14 | 55 65 | 28 | 39 |
串联起来81,,22,73,93,34,415,5,56,28,39,
然后将上面的一串数按十位数再分配到0-9的10个桶中:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
14 | 22 28 | 39 | 43 | 55 | 65 | 73 | 81 | 93 |
这样串联起来就得到排序结果:14,22,28,39,34,55,65,73,81,93。
不难看出这个基数排序的时间复杂度是O(d(n+rd)),其中d是关键字的个数,n是待排序元素的个数,rd是关键字的取值范围。在这个例子中d=2,rd=10,n=10。