Skip to content

Commit 8aab664

Browse files
committed
feat: 归并排序
1 parent 9caca26 commit 8aab664

File tree

3 files changed

+195
-0
lines changed

3 files changed

+195
-0
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* 递归法(自顶向下)
3+
* 返回新数组
4+
* 这个方法在代码上理解起来更容易
5+
* 但是需要开辟left和right数组的内存空间,且排序后的是一个新数组,非原数组
6+
*/
7+
8+
/**
9+
* 归并排序
10+
* 1、将包含n个数字的数组分成n个子数组,每个子数组中仅有一项,此时可认为它是有序的
11+
* 2、两个有序的子数组两两合并,得到一个有序的数组
12+
* 3、重复进行2步骤,最终得到一个数组,这个数组就是有序的
13+
*/
14+
15+
/**
16+
* 合并两个子序列
17+
*/
18+
const merge = (left, right) => {
19+
const result = [];
20+
21+
while (left.length && right.length) {
22+
// 因为left和right都是有序的,比较两个数组中谁的头部数字小,就先放进结果数组,保证result是从小到大的顺序
23+
if (left[0] < right[0]) {
24+
result.push(left.shift());
25+
} else {
26+
result.push(right.shift());
27+
}
28+
}
29+
30+
// 当一个数组已经空了,就把另一个数组依次push进result中
31+
while (left.length) {
32+
result.push(left.shift());
33+
}
34+
35+
while (right.length) {
36+
result.push(right.shift());
37+
}
38+
39+
return result;
40+
};
41+
42+
const mergeSort = (arr) => {
43+
const len = arr.length;
44+
// 如果仅有1项或者0项,无需排序
45+
if (len < 2) return arr;
46+
47+
// 在中间位置,将序列分成两个子序列
48+
const mid = Math.floor(len / 2);
49+
const left = arr.slice(0, mid);
50+
const right = arr.slice(mid);
51+
52+
// 使两个子数组有序,
53+
// 再将这两个有序子数组合并成一个有序的数组
54+
return merge(mergeSort(left), mergeSort(right));
55+
};
56+
57+
// test case:
58+
const arr = Array.from({ length: 30 }).map(() =>
59+
Math.floor(Math.random() * 1000)
60+
);
61+
console.log(mergeSort(arr));
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* 递归法(自顶向下)
3+
* 对原数组排序(不返回新数组)
4+
*/
5+
6+
/**
7+
* 合并两个子序列
8+
* 子序列是数组中的[left, right]区间内以mid为界被分开的两个子序列
9+
*/
10+
const merge = (arr, left, mid, right) => {
11+
let i = left;
12+
let j = mid + 1;
13+
// 用于临时存放合并好的结果
14+
const result = [];
15+
16+
// 判断两个子序列的头部元素,哪个更小,就优先放进result数组
17+
while (i <= mid && j <= right) {
18+
if (arr[i] <= arr[j]) {
19+
result.push(arr[i++]);
20+
} else {
21+
result.push(arr[j++]);
22+
}
23+
}
24+
25+
// 将剩下的一个子序列中的剩余元素依次放进result数组
26+
while (i <= mid) {
27+
result.push(arr[i++]);
28+
}
29+
while (j <= right) {
30+
result.push(arr[j++]);
31+
}
32+
33+
// 将result中保存的元素依次复制到arr中的对应位置
34+
// 此处我们从后往前复制
35+
while (right >= left) {
36+
arr[right] = result[right - left];
37+
right--;
38+
}
39+
};
40+
41+
const sort = (arr, left, right) => {
42+
// 如果子序列仅有1项或者0项,无需排序
43+
if (right <= left) return;
44+
45+
const mid = left + Math.floor((right - left) / 2);
46+
47+
// 使两个子数组有序,
48+
sort(arr, left, mid);
49+
sort(arr, mid + 1, right);
50+
// 再将这两个有序的子序列合并成一个大的有序序列
51+
merge(arr, left, mid, right);
52+
};
53+
54+
const mergeSort = (arr) => {
55+
sort(arr, 0, arr.length - 1);
56+
};
57+
58+
// test case:
59+
const arr = Array.from({ length: 30 }).map(() =>
60+
Math.floor(Math.random() * 1000)
61+
);
62+
mergeSort(arr);
63+
console.log(arr);
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* 迭代法(自底向上)
3+
*/
4+
5+
/**
6+
* 合并两个子序列
7+
* 子序列是数组中的[left, right]区间内以mid为界被分开的两个子序列
8+
*/
9+
const merge = (arr, left, mid, right) => {
10+
let i = left;
11+
let j = mid + 1;
12+
// 用于临时存放合并好的结果
13+
const result = [];
14+
15+
// 判断两个子序列的头部元素,哪个更小,就优先放进result数组
16+
while (i <= mid && j <= right) {
17+
if (arr[i] <= arr[j]) {
18+
result.push(arr[i++]);
19+
} else {
20+
result.push(arr[j++]);
21+
}
22+
}
23+
24+
// 将剩下的一个子序列中的剩余元素依次放进result数组
25+
while (i <= mid) {
26+
result.push(arr[i++]);
27+
}
28+
while (j <= right) {
29+
result.push(arr[j++]);
30+
}
31+
32+
// 将result中保存的元素依次复制到arr中的对应位置
33+
// 此处我们从后往前复制
34+
while (right >= left) {
35+
arr[right] = result[right - left];
36+
right--;
37+
}
38+
};
39+
40+
const mergeSort = (arr) => {
41+
const len = arr.length;
42+
if (len < 2) return;
43+
44+
// 子序列元素个数,为1时,子序列仅有1个元素,如[3]和[5]合并,以此类推
45+
let k = 1;
46+
47+
while (k < len) {
48+
// 两个完整子序列(完整:子序列元素个数为k)合并完后的元素个数
49+
const count = 2 * k;
50+
let i;
51+
// i < len - count是为了保证在这个for循环中的合并都是完整子序列合并
52+
for (i = 0; i < len - count; i += count) {
53+
merge(arr, i, i + k - 1, i + count - 1);
54+
}
55+
56+
// 最后可能存在一个子序列,则不需要合并,因为每个子序列已是有序的;
57+
// 最后如果存在两个子序列(i + k < len 的情况),两个子序列可能不全是完整子序列,比如[3,5,8,16]和[2,7],
58+
// 如果按照上面的for循环中right传入i + count - 1的话,将会超出arr的长度,故单独处理
59+
if (i + k < len) {
60+
merge(arr, i, i + k - 1, len - 1);
61+
}
62+
k *= 2;
63+
}
64+
};
65+
66+
// test case:
67+
const arr = Array.from({ length: 90 }).map(() =>
68+
Math.floor(Math.random() * 1000)
69+
);
70+
mergeSort(arr);
71+
console.log(arr);

0 commit comments

Comments
 (0)