在学习归并排序之前,有必要了解分治法,因为归并排序正是应用了分治模式。(基本定义摘自《算法导论》)
一、分治法
1、思想:
将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后合并这些子问题的解来建立原问题的解。
2、三个步骤
分治法在每层递归时有三个步骤:
(1)分解:
分解原问题分解为若干子问题,这些子问题是原问题的规模较小的实例。
**举例:**比如,全班同学随意排成了一行,这时高矮顺序是乱的,现在要按身高排序,可以把问题分解为把前一半按身高排好序,后一半也按身高排好序两个子问题。
**另外:**若干表明子问题不一定是2个,若是2个则是归并排序。
(2)解决
解决这些子问题,递归地求解各子问题。然而,若子问题的规模足够小,则直接求解。
举例: 分别解决给前一半和后一半排序的子问题。比如,给前一半排序,又再次将问题分解为给前1/4排序和给1/4到1/2的部分排序,解决这两个子问题后采用合并算法合并就得到前一半的身高排序。
事实上,给前1/4排序又可以再次分解…
直到分解为单个同学排序,这时无须再分解,排序后的序列就是他自己。
(3)合并
合并这些子问题的解成原问题的解。
**举例:**采用合并算法将已排好序的前一半队列和后一半队列合并,就得到所有人的身高排序。
二、归并排序
归并排序完全遵循分治模式。
1、步骤:
(1)分解:分解待排序的n个元素的序列成各具n/2个元素的两个子序列。
(2)解决:使用归并排序递归地排序两个子序列。
(3)合并:合并两个已排序的子序列以产生已排序的答案。
2、关键:
“合并”步骤中两个已排序序列的合并。
3、合并算法:
假设这时前一半和后一半各自都已经排好序,最矮的都在各部分的最前面,这时只需合并两个排序就可以得到全班的排序。合并算法类似这样:
(1)假设前一半队列最矮的是甲,后一半队列中最矮的是乙,比较甲和乙,两者中最矮的命名为a,将a放到合并队列中,这时合并队列中只有a一人。
(2)再次比较前一半队列和后一半队列中最矮的同学,假设现在b最矮,将b放到合并队列中a的后面,这时合并队列有a、b两个人。
(3)…
(4)直到前一半队列或者后一半队列没人。假设前一半队列没人了,现在后一半队列中剩下的同学都比已合并的同学高,直接将剩余的同学依次排到已合并的队列后面,排序完成。
三、我的JavaScript实现
function merge_sort(arr) {
if (arr.length>1) {
var array=[];
var l=Math.floor(arr.length/2);
for (var i = 0; i < l; i++) {
array.push(arr.pop());
}
merge_sort(arr);
merge_sort(array);
merge(arr, array);
}else{
return arr;
}
}
function merge(arr1,arr2) {
var arr=[];
while(arr1.length&&arr2.length){
if (arr1[0]<=arr2[0]) {
arr.push(arr1.shift());
}else{
arr.push(arr2.shift());
}
}
if (arr1.length) {
for (var i = 0; i < arr1.length; i++) {
arr.push(arr1.shift());
}
}else if (arr2.length) {
for (var j = 0; j < arr2.length; j++) {
arr.push(arr2.shift());
}
}
var l=arr.length;
for (var i = 0; i < l; i++) {
arr1.push(arr.shift());
}
}
var a=[11,2,3,445,7,32,71,1,94];
merge_sort(a);
console.log(a);
var b=[94,11];
merge_sort(b);
console.log(b);
以上代码还有可以优化的空间,对比了下其他人的实现,若采用slice似乎要简单一些。