合并排序
将排序数组拆分成若干个子数组, 然后进行合并
Space ~ O(n)
第一步合并两个已排序数组
- 方法一
对所有元素进行插入排序
- 方法二
对所有元素执行 Array.sort ---不在算法考量内
- 方法三
运用分治策略,拆分成两部分进行合并
假设现有数组[11,33,55,22,44]
图解分析(寻找循环不变式)
A | A1 | A2 | k=A回写位置,i=A1,j=A2 |
[11,33,55,22,44] | [11,33,55,Infinity] | [22,44,Infinity] | k=0; i=0; j=0 |
[11,33,55,22,44] | [11,33,55,Infinity] | [22,44,Infinity] | k=1; i=1; j=0 |
[11,22,55,22,44] | [11,33,55,Infinity] | [22,44,Infinity] | k=2; i=1; j=1 |
[11,22,33,22,44] | [11,33,55,Infinity] | [22,44,Infinity] | k=3; i=2; j=1 |
[11,22,33,44,44] | [11,33,55,Infinity] | [22,44,Infinity] | k=4; i=2; j=2 |
[11,22,33,44,55] | [11,33,55,Infinity] | [22,44,Infinity] | k=5; i=3; j=2 |
A1,A2分别添加Infinity 无穷大,是个哨兵位置,保证最后一个可比较回写的值必定比另一个区域的最后一个值小,当A中k指向的回写位置超出数组位置,终止循环.
代码实现合并两个有序数组
function merge(A, l, p, r){
//l 数组左边界
//p 数组分界位置
//r 数组右边界 (边界位置左闭右开)
let A1 = A.slice(l,p), A2 = A.slice(p,r);
A1.push(Infinity)
A2.push(Infinity) //设置哨兵位置
for(let k=l, i=0, j=0; k<r; k++){
A[k] = A1[i]<A2[j]?A1[i++]:A2[j++]
}
}
合并排序
- 合并排序的步骤
1.把需要处理的数组进行拆分,直至最小粒度为单个元素(单个元素即有序的数组如 [1] )
2.然后开始对每个有序数组进行合并即上述的合并两个已排序数组
3.合并完成后即已完全排列的数组
- 图表分析
步骤一:拆分 | 步骤二:合并 |
[11,33,55,77,99,22,44,66] | [11] [33] [55] [77] [99] [22] [44] [66] |
[11,33,55,77] [99,22,44,66] | [11,33] [55,77] [22,99] [44,66] |
[11,33] [55,77] [99,22] [44,66] | [11,33,55,77] [22,44,66,99] |
[11] [33] [55] [77] [99] [22] [44] [66] | [11,22,33,44,55,66,77,99] |
- 代码实现
function merge(A, l, p, r){
let A1 = A.slice(l, p), A2 = A.slice(p, r);
A1.push(Infinity)
A2.push(Infinity)
for(let k=l, i=0, j=0; k<r; k++){
A[k] = A1[i]<A2[j]?A1[i++]:A2[j++]
}
}
function mergeSort(A, l, r){
if(r-l < 2) {return} //如果长度小于2即只有一个或零个长度 本身有序不用排序
const p = Math.ceil((l+r)/2); //获取一个中心位置进行拆分
mergeSort(A, l, p) //左半部分持续拆分
mergeSort(A, p, r) //右半部分持续拆分
merge(A, l, p, r) //拆分完成进行合并
}