归并排序
选择排序的复杂度是O(nlogn)级别,归并排序利用的递归。
// 归并排序
function mergeSort(arr) {
// 处理从第0个位置开始,到数组的最后一个位置
__mergeSort(arr, 0, arr.length - 1);
return arr;
}
// 对[l, r]区间的元素进行排序
function __mergeSort(arr, l, r) {
// 递归到底的情况
if (l >= r) {
return;
}
// 在需要排序的元素数量较少时,可以转为使用插入排序,因为元素数量较少时,插入排序会更快
// if (r - l <= 15) {
// insertSortRange(arr, l, r)
// return
// }
// 计算区间的中点位置
let mid = Math.floor((l + r) / 2);
// 对[l, mid]区间的元素进行排序
__mergeSort(arr, l, mid);
// 对[mid + 1, r]区间的元素进行排序
__mergeSort(arr, mid + 1, r);
// 只有当mid位置元素大于mid + 1位置元素时才需要排序
if (arr[mid] > arr[mid + 1]) {
// 对[l, mid]和[mid + 1, r]两个部分进行归并
__merge(arr, l, mid, r);
}
}
function __merge(arr, l, mid, r) {
// 创建一个新数组,方便赋值
let space = [];
// 将[l, r]的元素都存储到新数组中
for (let i = l; i <= r; i++) {
space[i - l] = arr[i];
}
// i表示左半部分正在对比的元素,j表示右半部分正在对比的元素
let i = l,
j = mid + 1;
// 判断[l, r]的所有元素
for (let k = l; k <= r; k++) {
// 左半部分已经归并完成,没有元素时
if (i > mid) {
arr[k] = space[j - l];
j++;
} else if (j > r) {
// 右半部分已经归并完成,没有元素时
arr[k] = space[i - l];
i++;
} else if (space[i - l] < space[j - l]) {
// 左半部分元素小于右半部分元素
arr[k] = space[i - l];
i++;
} else {
// 右半部分元素小于左半部分元素
arr[k] = space[j - l];
j++;
}
}
}
// 自底向上归并
function mergeSortBtoU(arr) {
// 从底部开始归并,第一次归并2个元素,第二次归并4个元素,第三次归并8个元素,以此类推
for (let size = 1; size <= arr.length; size += size) {
// i + size可能超过arr.length的大小,所以循环结束条件为i + size小于arr.length
for (let i = 0; i + size < arr.length; i += size + size) {
// i + size + size - 1可能超过arr.length的大小,所以要取两者的较小一方
__merge(arr, i, i + size - 1, Math.min(i + size + size - 1, arr.length - 1));
}
}
return arr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72