归并排序属于稳定的比较排序算法。
归并排序由冯诺依曼于 1945 年首次提出,是基于分治法的一个非常典型的应用。
在归并排序每一层的基本步骤如下:
- 分解(Divide):将 n 个元素分成两个含 n/2 个元素的子序列
- 解决(Conquer):用合并排序法对两个子序列递归地进行排序
- 归并(Merge):合并两个已排序的子序列,得到排序结果
实现逻辑
实现逻辑分两种:“自上而下”的递归法和“自下而上”的迭代法。
递归法(Top-down)
- 申请空间,大小为两个已经排好序的序列之和;该空间用来存放合并后的序列;
- 设定两个指针,初始位置分别为两个已经排序的序列的起始位置;
- 比较两个指针所指向的元素,选择相对较小(升序)/大(降序)的元素放入到上述的合并空间,并移动指针到下一位置;
- 重复步骤 3,直至其中一个指针到达该序列尾;
- 将另一序列剩下所有元素直接复制到合并空间尾。
迭代法(Bottom-up)
假设序列中有 n 个元素:
- 将序列中每相邻的两个数字进行归并操作,形成
个序列,排序后每个序列包含一个或两个元素; - 如果此时序列数不为 1,则将上述的序列再次合并,形成
个序列,每个序列包含三个或四个元素; - 重复步骤 2,直到所有元素排序完毕,即序列数为 1。
伪代码
1 | void sort(T E, int n) { |
merge()
的时间复杂度为
而整个算法所花费时间:
可知当
此时的分解方法为对半分,平均复杂度为
当
二路归并代码实现
思路:
- 首先将每两个相邻的大小为 1 的子序列归并
- 然后对上一次归并所得到的大小为 2 的子序列进行相邻归并
- 直至最后归并到一个序列
C++:
1 | // 自上而下 |
1 | // 自下而上 |
1 | // 递归法,核心代码 |
Java:
1 | import java.util.Arrays; |
其中,自下而上的方法如下图所示: