首页 > 编程知识 正文

合并排序的最好情况,归并排序快速排序

时间:2023-05-05 23:04:59 阅读:228439 作者:131

合并排序,大致思想便是先将数组中的元素拆分成若干小部分,然后再将这些小部分按照顺序进行重新组合,从而实现排序。很明显,这里用到了分治法的思想,即将一个大问题分成若干个相同的小问题,因为问题规模变小了,所以解决问题的难度也随之减小,试想一下,对一个拥有10000个元素的数组进行排序简单还是对一个只拥有1个元素的数组进行排序简单?答案很明显。

对于合并排序,将大问题分成若干小问题是一件很容易的事情,只需要不断递归即可,其中最主要的难点就是将这些小问题解决之后,如何再将这些小问题合并起来,若是处理不好,很可能就导致算法失败。那么接下来就开始正式的讲解。

先上伪代码

因为伪代码没有其他语言的语法限制,能够有助于我们更好地将注意力放在算法上,所以这里我们先用伪代码来讲述一下主要思想:

algorithm mergeSort(A[0..n-1])//递归调用mergeSort来对数组A[0..n-1]排序//输入:一个可排序的数组A[0..n-1]//输出:非降序排列的数组A[0..n-1]if n > 1 //将数组中的n个元素分成n组copy A[0..(n/2)] to B[0..(n/2)]copy A[(n/2)..n-1] to C[0..(n/2)-1]mergeSort(B[0..(n/2)])mergeSort(C[0..(n/2)-1])merge(B, C, A) //将B和C合并到A中algorithm merge(B[0..n-1], C[0..m-1], A[0..n+m-1])//将两个有序数组B和C合并到A中//输入:两个有序数组和一个待整合数据的数组//输出:A[0..n+m-1]中已经有序存好了B和C中的数据i <- 0;j <- 0;k <- 0while i < n and j < m doif B[i] <= C[j]A[k] <- B[i]i <- i + 1else A[k] <- C[j]j <- j + 1k <- k + 1if i = n //说明B中的所有元素已经存放到A中,即C中的部分元素为存入到A中copy C[j..m-1] to A[k..n+m-1]else copy B[i..n-1] to A[k..n+m-1]

因为不管怎样,一个元素的数组绝对是最好排序的,因为不需要排序,所以我们一开始就需要将整个数组分成n个组,之后再慢慢地合并,如下图便是合并排序的一个例子:

代码实现

这里我是用的C++来实现该算法:

关键部分:

void merge(int* B, int lenB, int* C, int lenC, int* A, int lenA) {int i = 0, j = 0, k = 0;while (i < lenB && j < lenC) {if (B[i] <= C[j]) A[k++] = B[i++];else A[k++] = C[j++];}if (i == lenB) {while (j < lenC) A[k++] = C[j++];} else {while (i < lenB)A[k++] = B[i++];}}void mergeSort(int* A, int lenA) {if (lenA > 1) {int n1 = lenA / 2;int n2 = lenA - n1;int* B = (int*)malloc(sizeof(int) * n1);int* C = (int*)malloc(sizeof(int) * n2);for (int i = 0; i < n1; i++) {B[i] = A[i];}for (int i = 0; i < n2; i++) {C[i] = A[n1 + i];}mergeSort(B, n1);mergeSort(C, n2);merge(B, n1, C, n2, A, lenA);free(B);free(C);}}

然后在main()函数中调用:

int main() {cout << "排序前的数组:" << endl;for (int i = 0; i < 10; i++) {cout << A[i] << " ";}cout << endl;mergeSort(A, 10);cout << "排序后的数组:" << endl;for (int i = 0; i < 10; i++) {cout << A[i] << " ";}cout << endl;return 0;}

运行结果如下:

复杂度分析

合并排序的复杂度要分成两部分来看,一部分是分治,另一部分则是合并,

其中合并部分,最差执行n-1次比较,最优执行n/2次比较,都是O(n),

因此得到如下公式:
T ( n ) = { 0 , n = 1 2 T ( n / 2 ) + O ( n ) , n > 1 T(n)=begin{cases} 0 quadquadquadquadquadquad,n=1\ 2T(n/2)+O(n), n>1end{cases} T(n)={0,n=12T(n/2)+O(n),n>1​
再由主定理可得合并排序的时间复杂度为O(nlogn),并且无论是最优最差还是平均,复杂度都是O(nlogn)。

接下来我们再来看看合并排序的空间复杂度,

我们同样可以得到如下公式:
S ( n ) = { 0 , n = 1 2 S ( n / 2 ) + O ( n ) , n > 1 S(n)=begin{cases} 0 quadquadquadquadquadquad,n=1\ 2S(n/2)+O(n), n>1end{cases} S(n)={0,n=12S(n/2)+O(n),n>1​
所以我们也可以马上知道合并排序的空间复杂度也为O(nlogn)。

由此我们也可以发现,虽然合并排序的时间复杂度比较好,但是空间复杂度确实太高了,相比冒泡排序、选择排序这些在位排序而言,占用的额外空间实在是太多了。

因此针对合并排序的改进方向便是减小空间复杂度。

参考资料

《算法设计与分析基础》第三版以及老师的课件

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。