从零开始学算法 - 归并排序
归并的思路:将大问题拆解成小问题,小问题单独解决后,再合并成大问题。
翻译成排序:将大数组拆解成小数组,小数组单独排序后,再合并成大数组。
用一个长度为10的数组,解释一下归并的过程:
原始数组:[72, 34, 51, 80, 14, 92, 84, 95, 68, 22]
对半拆分,直到数组变成长度为1:
对半拆分:[72, 34, 51, 80, 14],[92, 84, 95, 68, 22]
继续拆分:[72, 34],[51, 80, 14],[92, 84],[95, 68, 22]
继续拆分:[72],[34],[51],[80, 14],[92],[84],[95],[68, 22]
最后一次:[72],[34],[51],[80],[14],[92],[84],[95],[68],[22]
根据刚才的分组,将拆好的数组按顺序装到大数组中:
开始归并:[34, 72],[51],[14, 80],[84, 92],[95],[22, 68]
继续归并:[34, 72],[14, 51, 80],[84, 92],[22, 68, 95]
继续归并:[14, 34, 51, 72, 80],[22, 68, 84, 92, 95]
最后一次:[14, 22, 34, 51, 68,72, 80, 84, 92, 95]
将上述过程翻译成代码:
拆分的过程:
function depart(arr){ var len = arr.length; //长度到1了,就好了,可以return if(len<2){ return arr; } //长度没到1,就继续拆 var middle = Math.floor(len/2); var left = arr.slice(0,middle); var right = arr.slice(middle); //拆成左右两组后,继续递归拆分 depart(left); depart(right); }
归并的过程:
function merge(left,right){ //创建一个数组,存归并后的结果 var result = []; //左右数组均有值时,依次把较小的push到新数组中 while(left.length && right.length){ if(left[0]<=right[0]){ result.push(left.shift()); }else{ result.push(right.shift()); }
} //某个数组没值了,把剩余数组的剩余值依次push到新数组中 while(left.length){ result.push(left.shift()); } while(right.length){ result.push(right.shift()); } }
把两个过程衔接起来,就是归并排序啦:
//后续步骤要用前一步的结果,所以记得加返回值,其他步骤和前面一样
function depart(arr){ var len = arr.length;
if(len<2){
return arr;
} var middle = Math.floor(len/2); var left = arr.slice(0,middle); var right = arr.slice(middle);
//把左右数组传给归并方法 return merge(depart(left),depart(right)); } function merge(left,right){ var result = []; while(left.length && right.length){ if(left[0]<=right[0]){ result.push(left.shift()); }else{ result.push(right.shift()); } } while(left.length){ result.push(left.shift()); } while(right.length){ result.push(right.shift()); }
return result; }