各种排序比较

来源:互联网 发布:淘宝店全球购是真的吗 编辑:程序博客网 时间:2024/05/29 02:35

集合num = {a1,a2,...an},将其按非递减排序。

1、插入排序

//increasingvoid insert_sort(int num[],int n){for(int i = 1;i < n;i++){int j = i,tmp = num[i];for(;j && num[j - 1] > tmp;j--)num[j] = num[j - 1];num[j] = tmp;}}
最好情况:num[]排序前就是非递减,那么时间复杂度为O(n)

最坏情况:num[]排序前为非递增,那么时间复杂度为O(n^2)

所以插入排序时间复杂度为O(n^2),空间复杂度为O(1)


2、选择排序

void select_sort(int num[],int n){for(int i = 0;i < n;i++)for(int j = i + 1;j < n;j++)if(num[i] > num[j])swap(num[i],num[j]);}
最好情况:num[]排序前就是非递减,那么时间复杂度为O(n)

最坏情况:num[]排序前为非递增,那么时间复杂度为O(n^2)

所以选择排序时间复杂度为O(n^2),空间复杂度为O(1)


3、冒泡排序

void bubble_sort(int num[],int n){for(int i = 0;i < n;i++)for(int j = n - 1;j >= i + 1;j--)if(num[j - 1] > num[j])swap(num[j],num[j - 1]);}
最好情况:num[]排序前就是非递减,那么时间复杂度为O(n)

最坏情况:num[]排序前为非递增,那么时间复杂度为O(n^2)

所以冒泡排序时间复杂度为O(n^2),空间复杂度为O(1)


4、归并排序

/* * p <= q < r * num[p...q]及num[q + 1...r]已经有序 */void merge(int num[],int p,int q,int r){int tmp[N],n,i,j;n = 0;i = p,j = q + 1;while(i <= q && j <= r)if(num[i] < num[j])tmp[n++] = num[i++];elsetmp[n++] = num[j++];while(i <= q)tmp[n++] = num[i++];while(j <= r)tmp[n++] = num[j++];for(i = 0;i < n;i++)num[i + p] = tmp[i];}/* * 将num[p...q]排序 */void merge_sort(int num[],int p,int r){if(p < r){int q = (p + r) / 2;merge_sort(num,p,q);merge_sort(num,q + 1,r);merge(num,p,q,r);}}
因为均为num[]已经有序与否,merge()函数一定是要执行的,它的时间复杂度为O(n),所以合并排序最好与最坏的时间复杂度均为为O(nlgn),合并排序的时间复杂度就为O(nlgn),它的空间复杂度为O(n)。


5、堆排序

int left(int x){return x << 1;}int right(int x){return (x << 1) + 1;}int parent(int x){return x >> 1;}/* *递归式:O(lgn) *//*void max_heapify(int num[],int i,int heapsize){int r,l,lmax;l = left(i),r = right(i);if(l <= heapsize && num[i] < num[l])lmax = l;elselmax  = i;if(r <= heapsize && num[lmax] < num[r])lmax = r;if(lmax != i){swap(num[i],num[lmax]);max_heapify(num,lmax,heapsize);}}*/void max_heapify(int num[],int i,int heapsize){int r,l,lmin,res;res = num[i];while(1){l = left(i),r = right(i);if(l > heapsize)//无左结点,即此时i为叶子结点break;lmin = l;if(r <= heapsize && num[lmin] < num[r])lmin = r;if(res >= num[lmin])//res比两棵子树的根都要小,就不需要调整了break;num[i] = num[lmin];i = lmin;}num[i] = res;}/* * num[n / 2]为倒数第一个非叶子结点 */void build_max_heap(int num[],int n){for(int i = n / 2;i >= 1;i--)max_heapify(num,i,n);}void heap_sort(int num[],int n){int heapsize = n;build_max_heap(num,n);for(int i = n;i >= 2;i--){swap(num[1],num[heapsize]);max_heapify(num,1,--heapsize);}}
max_heapify()函数的时间复杂度为O(lgn),堆排序最好与最好坏情况的时间复杂度均为O(nlgn),因为是原地排序,所以空间复杂度为O(1)。


6、快速排序

int partition(int num[],int low,int high){int key = num[low];while(low < high){while(low < high && num[high] > key)high--;num[low] = num[high];while(low < high && num[low] <= key)low++;num[high] = num[low];}num[low] = key;return low;}void quick_sort(int num[],int p,int r){if(p < r){int q = partition(num,p,r);quick_sort(num,p,q - 1);quick_sort(num,q + 1,r);}}
快速排序也应用了分治的思想,在partition()函数中每次都将第一个数作为关键字比较。快速排序的最坏情况是当num[]已经是非递减有序的时候,此时 partition()函数每次都要执行high - low次,所以此时时间复杂度退化为O(n^2),而快排一般的复杂度为O(nlgn),空间复杂度为O(lgn),即递归时栈所用。


7、快速排序的随机化版本

基本的快速排序当数据基本有序时,时间复杂度退化为O(n^2),其实这里我们可以进行一个随机优化。即partition()函数在取关键字key时,并不是取第一个,而是从num[low...high]中随机取一个作为关键字,这样就可以避免最坏情况。

int random_partition(int num[],int low,int high){if(low < high){srand((unsigned)time(NULL));int tmp = rand() % (high - low)+ low;swap(num[low],num[tmp]);}return partition(num,low,high);}void random_quick_sort(int num[],int p,int r){if(p < r){int q = random_partition(num,p,r);random_quick_sort(num,p,q - 1);random_quick_sort(num,q + 1,r);}}


8、计数排序

计数排序假设n个输入元素中的每一个元素都是介于0到k之间的整数,k为某个整数。运用hash法,计算出每个元素在所有元素中的位置,最后直接放入到数组相应的位置上。

/* * 要排序的数在num[]中,都是在0~1000之间,排完序后存入num1 */void count_sort(int num[],int num1[],int n){int tmp[1001];memset(tmp,0,sizeof(tmp));for(int i = 0;i < n;i++)tmp[num[i]]++;for(int i = 1;i < 1001;i++)tmp[i] += tmp[i - 1];for(int i = 0;i < n;i++)num1[--tmp[num[i]]] = num[i];}
计数排序不是比较排序算法,它的 时间复杂度为O(k + n),当k = O(n)时,它的复杂度即为O(n)。空间复杂度为O(k + n)。


9、桶排序

跟计数排序一样,桶排序也对输入亻了某种假设。桶排序假设输入由一个随机过程产生,该过程将元素均匀而独立地分页在区间[0,1)上。并把[0,1)区间分成n个大小相同的子区间,或称桶。然后将n个输入数据分页到各个桶当中去。然后对各个桶中的数据进行排序,最后按次序把各桶中的数据列出来即可。

void bucket_sort(double num[],int n){double tmp[100][100];memset(tmp,0,sizeof(tmp));for(int i = 0;i < n;i++){int t = int(floor(num[i] * n));tmp[t][0] += 1;tmp[t][(int)tmp[t][0]] = num[i];}for(int i = 0;i < n;i++)if(tmp[i][0]){for(int j = 1;j <= (int)tmp[i][0];j++)for(int k = j + 1;k <= (int)tmp[i][0];k++)if(tmp[i][j] > tmp[i][k])swap(tmp[i][j],tmp[i][k]);}int t = 0;for(int i = 0;i < n;i++)for(int j = 1;j <= (int)tmp[i][0];j++)num[t++] = tmp[i][j];for(int i = 0;i < n;i++)printf("%.3lf ",num[i]);puts("");}
桶排序关键时间关键就在第2个for循环,《算法导论》上讲,桶排序的期望时间是线性的。

各排序方法时间效率比较(时间单位ms)

1、20次每次随机生成10000组数据

数据插入排序选择排序冒泡排序归并排序堆排序快速排序计数排序119660693743302196607922333031706099493330417861894433305172622964333061766539774330717761595633308177625954432191736219554320101766309534320111756209553321121746329524321131736069483420141686139583330151806299553340161786449543330171736069514320181716149414320191736129533320201766229463420平均176.60620.20951.203.403.102.550.00


2、20次每次随机生成100000组数据

数据插入排序选择排序冒泡排序归并排序堆排序快速排序计数排序117534502069592739413232173895017295039404829331775449930949444042312417380497629475339403125172404938995831404130361792750816951113940292717132493369507939402938171664937895925394033391722449202971153940292101788150673973343940293111771552176969183941202121739250451962503840292131744849394959544042312141715149347965853940292151763850890952983940292161757549348944303940292171718849233941494044303181718850872964104241293191746250807969334041323201761950601966713940292平均17450.1550099.1595832.8039.441.0529.452.45

从表中可以看出,计数排序的平均时间是最好的,当然,计数排序需要有数据形式的限制。除了计数排序,快速排序的平均时间最好,而冒泡排序最差。


原创粉丝点击