归并排序
- 把 2 个及以上的有序子序列,"归并" 成一个有序序列
- 内部排序中,多采用 二路 归并排序
- 整个归并排序只需要 logN 躺
-
基本思想:
- 切分步骤:先将大数组,拆分成 2 组,然后 2 组再拆分,直到元素个数为 1,
- 合并步骤:接着两个 单一元素比较合并,合并完再合并 两个 二元素,接着 两个 四元素
-
具体思路:
- 递归切分数组
- 若当前数组 数量小于 1,无需排序,直接返回结果
- 否则当前数组分成 2 个子数组,递归排序这 2 个子数组
- 在子数组排序结束后,将子数组的结果归并成排好序的数组
-
关键问题:
- 如何将两个有序序列,合并成一个?
- 分别用指针指向两个不同的有序序列,比较当前指针所在的元素,哪个小,移出哪个,且移出所在指针前进一位重复比较
- 若 一方 已为空,则直接把另一方有序序列,全部接在后面
- 如何将两个有序序列,合并成一个?
-
时间复杂度: O(n logn)
-
空间复杂度: O(n),因为需要一个与原始序列同样大小的辅助序列
let list = [8, 4, 5, 7, 1, 3, 6, 2];
let temp = [];
function MergeSort(list, start, end) {
if (end - start < 1) return; //为了细化分到单个元素,所以不需要 =
// 分 (大化小)
let mid = Math.floor((start + end) / 2);
MergeSort(list, start, mid);
MergeSort(list, mid + 1, end);
// 治 (从单个元素开始合并成有序)
Merge(list, start, mid, end, temp);
}
MergeSort(list, 0, list.length - 1);
console.log(list);
function Merge(list, start, mid, end, temp) {
let l_start = start;
let r_start = mid + 1;
let i = 0;
// mid实则为 l_end, end实则为r_end
while (l_start <= mid && r_start <= end) {
//比较 左右两边,将其按顺序塞进 temp数组里
if (list[l_start] < list[r_start]) {
temp[i++] = list[l_start++];
} else {
temp[i++] = list[r_start++];
}
}
//如果 一边完了,另一边还有的话,直接全部塞进 temp里
while (l_start <= mid) {
temp[i++] = list[l_start++];
}
while (r_start <= end) {
temp[i++] = list[r_start++];
}
//temp已经是有序了,这里利用temp将list对应的下标调整为有序
//这样做了,才可以让 下一次递归是建于 list有序 的基础之上
i = 0;
while (start <= end) {
list[start++] = temp[i++];
}
}