持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天
归并排序
思想
归并归并,先递归拆分,后逐个排序并回,干讲概念不好理解,我们直接看例子:
待排序数组:[0, 1, 4 ,2 ,3, 6, 0]
第一步,递归拆分:
第一层递归:将数组一分为二,分为[0, 1, 4, 2]和[3, 6, 0]两部分
第二层递归:继续将第一层拆分出的数组再次拆分:左半部分拆分成组合([0, 1]、[4, 2])右半部分拆分成组合([3, 6]、[0])
第三层递归:将第二层拆分出的数组继续拆分,第二层的[0, 1]拆分为组合:([0]和[1]),[4, 2]拆分为组合:([4]和[2]),右侧同理,分别为[3]和[6]以及[0]
递归拆分的工作就是把数组不停从地中间划分,最后分到每个小数组中只有一个元素为止
第二步,排序并回:
将最后递归出的组合进行每组内的排序整合
比如[0]和[1]组合,按从小到大排,比较后并为[0, 1]
[4]和[2]的组合,比较后并为[2, 4]
同理,[3] [6]并为[3, 6],[0]没有和它一组的元素,自成为一组
第三层递归的组合排序归整完毕,接下来对第二层进行操作:
现在分组为[0, 1]和[2, 4],[3, 6]和[0]
同时从一组内的两个数组头起开始扫描,两指针指向的元素进行比较,小者先进入合并数组,0小于2,0先被归整,现在左侧数组中的指针移动,指向1,右侧指针不动,指向2,1小于2,故1被归整,左侧指针无指向元素,右侧元素按序加入归整组合中,最后排序成[0, 1, 2, 4]
右侧组合也同理,0先加入归整组合中,随后是3及6,得到[0, 3, 6]
接下来就是回到了第一层递归,对[0, 1, 2, 4]和[0, 3, 6]进行整合,还是两个指针同时扫描,小者先入 -> [0, 0] -> [0, 0, 1] -> [0, 0, 1, 2] -> [0, 0, 1, 2] -> [0, 0, 1, 2, 3] -> [0, 0, 1, 2, 3, 4] -> [0, 0, 1, 2, 3, 4, 6],至此,整个归并排序结束,得到有序序列
实现(javascript)
function mergeSort(arr){
if(arr.length < 2){
return arr;
}
let mid = Math.floor(arr.length/2)
let left = arr.slice(0, mid);
let right = arr.slice(mid);
//递归调用实现深层分组
return merge(mergeSort(left), mergeSort(right));
}
function merge(left, right){
let result = [];
while(left.length && right.length){
if(left[0] > right[0]){
result.push(right.shift());
}else{
result.push(left.shift());
}
}
while(left.length){
result.push(left.shift());
}
while(right.length){
result.push(right.shift());
}
return result;
}
时空复杂度及稳定性
平均时间复杂度:O(nlogn)
最好时间复杂度:O(n)
最差时间复杂度:O(nlogn)
空间复杂度:O(n)
稳定