归并排序的实现
1.概述
归并排序由冯·诺伊曼在1945年首次提出,是一种典型的分治思想:将问题拆分,递归处理,然后合并。归并排序的基础思路是,当一个数组的前后两部分都是有序的话,那么可以在O(n)时间内合并,使整个数组有序。归并排序的算法复杂度,在最好、最坏和平均的情况下,都是O(nLogn)。
2.算法
归并排序有两种思路:自顶向下和自底向上。
自顶向下就是先将数组作为整体,设数组长度为n,则先分成两个n/2的数组,递归调用归并排序,然后再合并。
按照这个思路,可以先写出以下递归代码:
1 |
|
而merge
的过程也很简单:
- 分配一个临时数组,空间大小和待合并的数组相同:
j-i+1
- 当左右两部分数组都有值时,对比两个数组中最小的值,将该值存放到临时数组中
- 当一个子数组的数据全部存放到临时数组后,将另一个子数组的数据依次放到临时数组中
- 将临时数组的数据替换到原数组中
在这个过程需要分配额外的空间,其大小和数组长度一致,所以归并排序的空间复杂度为O(n)
代码如下:
1 |
|
归并排序也可以采用自底向上的思路,即先将数组n分成一个个长度为1的子数组,然后两两合并;再分成长度为2的子数组,合并;直到分成长度为n/2的子数组,再进行合并,达到最终有序。
代码如下:
1 |
|
3.C++11代码
- 递归版本
1 |
|
- 非递归版本
1 |
|
4.复杂度分析
归并排序其实可以看作是二叉树的后序遍历,树的深度是log2n,每一层的merge,其比较的总次数都是数组长度n,因此总体的时间复杂度是O(nlogn)。
5.对比
归并排序和快速排序都是常见的基于比较的排序算法,它们的时间复杂度都为 O(nlogn) 。
归并排序的最好、最坏和平均时间复杂度都为O(nlogn),并且它具有稳定性。
归并排序的主要缺点是需要开辟额外的空间,这可能在处理大规模数据时限制了其使用。
快速排序的优势在于它是一种原址排序算法,不需要额外的空间开销,它的平均速度比归并排序更快,特别是对于大规模数据集。但是,快速排序在最坏情况下的运行时间仍然不理想,而且它不具有稳定性,可能会改变相同元素的原始相对位置。
因此,选择归并排序或快速排序取决于具体问题和数据特征。在排序需要保持稳定性的问题上,归并排序是最好的选择;在其他问题上,特别是处理大规模数据时,快速排序通常更为高效。