原理
- 将数组划分为n个子数组,划分程度为每个子数组仅包含一个元素(仅有一个元素的数组可以认为是有序的)
- 将这些子数组两两比较,合并为较大的数组,较大的数组中再进行比较合并,直至合并为唯一的数组
演示
摘自:维基百科(侵删)
我自己用画图绘制的图:
分析
归并排序方程式:
T(n) =
{
Θ
(
1
)
,
if
n
is 1
2
T
(
n
/
2
)
+
Θ
(
n
)
,
if
n
is more than 1
\begin{cases} \varTheta(1), & \text{if $n$ is 1} \\ 2T(n/2) + \varTheta(n), & \text{if $n$ is more than 1} \end{cases}
{Θ(1),2T(n/2)+Θ(n),if n is 1if n is more than 1
- 当n=1的时候,即数组规模为1时,不需要排序,即为1.
- 当n>1的时候,即数组规模大于1时,需要使用递归排序的方法,所以每次将数组规模n划分为两部分,于是出现了
n/2
,代入原先等式中,即T(n/2)
,因为大于1所以同时也包含为1的情况,所以要把为1时的情况加上。
每一层的总和都是 cn
,至于单一i
层的推导方式,这个缓缓。
共有
lg
n
\lg n
lgn 层,所以总时间复杂度为
c
n
(
lg
n
+
1
)
=
c
n
lg
n
+
c
n
cn(\lg n + 1) = cn\lg n + cn
cn(lgn+1)=cnlgn+cn。1
忽略到低阶项和常量c后,即可得到结果
Θ
(
n
lg
n
)
\varTheta(n\lg n)
Θ(nlgn)
-
我当时学习遇到一个问题,怎么知道层高 lg n \lg n lgn 的?
我们假设最顶层的cn是2的整数次幂,那么假设层高为h,那么 2 h = n 2^h = n 2h=n,进而 h = lg n = log 2 n h = \lg n= \log_2 n h=lgn=log2n
代码
Javascript
const list = [3, 24, 18, 57, 26, 9, 7]; // 待排序的数组
function mergeSort(arr) {
if (arr.length === 1) {
return arr;
}
let middle = parseInt(arr.length / 2);
let left = arr.slice(0, middle);
let right = arr.slice(middle);
console.log('');
console.log('%c arr: ', 'background: #D2DBEA; color: #fff', arr);
console.log('%c middle: ', 'background: #9EBBC3; color: #fff', middle);
console.log('%c left: ', 'background: #ABABAD; color: #fff', left);
console.log('%c right: ', 'background: #36A7CF; color: #fff', right);
console.log('');
return merge(mergeSort(left), mergeSort(right));
}
function merge(left, right) {
let result = [];
while (left.length && right.length) {
if (left[0] < right[0]) {
result.push(left.shift());
} else {
result.push(right.shift());
}
}
if (left.length) {
result = result.concat(left);
}
if (right.length) {
result = result.concat(right);
}
console.log(
'%c merge result: ',
'background: #073559; color: #fff',
result
);
console.log('');
return result;
}
mergeSort(list);
Java
伪代码:
摘自《算法导论》
import java.util.Arrays;
/**
* 归并排序
* @author simorel
* @date 2019/8/29
* @description
*/
public class MerageSort {
public static void main(String[] args) {
int[] list = { 3, 24, 18, 57, 26, 9, 7 };
mergeSort(list, 0, list.length - 1);
System.out.println();
System.out.println();
System.out.println(Arrays.toString(list));
}
/**
* 数组归并排序
* @param A 待排序数组
* @param p 左数组起始位置
* @param r 右数组结束位置
*/
public static void mergeSort(int[] A, int p, int r) {
if (p < r) {
int q = (p + r) / 2;
mergeSort(A, p, q);
mergeSort(A, q + 1, r);
merge(A, p, q, r);
}
}
/**
* 数组排序
* @param A 待排序数组
* @param p L左数组起始位置
* @param q L左数组结束位置
* @param r R右数组结束位置
* @return 排序好的数组
*/
public static void merge(int[] A, int p, int q, int r) {
int n1 = q - p + 1; // L 左数组长度
int n2 = r - q; // R 右数组长度
int[] L = new int[n1 + 1];
int[] R = new int[n2 + 1];
for(int i = 0; i < n1; i++) {
L[i] = A[p + i];
}
for(int i = 0; i < n2; i++) {
R[i] = A[q + i + 1];
}
L[n1] = Integer.MAX_VALUE;
R[n2] = Integer.MAX_VALUE;
System.out.println();
System.out.println("array: " + Arrays.toString(A));
System.out.println("p: " + p);
System.out.println("q: " + q);
System.out.println("r: " + r);
System.out.println("L: " + Arrays.toString(L));
System.out.println("R: " + Arrays.toString(R));
int i = 0;
int j = 0;
for (int k = p; k <= r; k++) {
if (L[i] <= R[j]) {
A[k] = L[i];
i++;
} else {
A[k] = R[j];
j++;
}
}
}
}
参考链接
[1] 百度百科 归并排序
[2] 维基百科 Merge sort
计算时间复杂度的时候为什么要加一,即 lg n + 1 \lg n + 1 lgn+1,因为还有n=1时的情况。 ↩︎