前面三种排序算法只有教学价值 ,因为效率低,很少实际使用。归并排序(Merge sort)则是一种被广泛使用 的排序方法。
将两个已经排序的数组合并 ,要比从头开始排序所有元素来得快。因此,可以将数组拆开,分成n个只有一个元素的数组,然后不断地两两合并,直到全部排序完成。
算法实现
let mergeSort = ( arr ) => {
if (arr. length < 2 ) return arr;
let middle = parseInt (arr. length / 2 ),
left = arr. slice ( 0 , middle),
right = arr. slice (middle);
return merge ( mergeSort (left), mergeSort (right));
}
let merge = ( left , right ) => {
let result = [];
while (left. length && right. length ) {
left[ 0 ] <= right[ 0 ] ?
result. push (left. shift ()) :
result. push (right. shift ());
}
while (left. length ) result. push (left. shift ());
while (right. length ) result. push (right. shift ());
return result;
}
function merge ( left , right ){
var result = [],
il = 0 ,
ir = 0 ;
while (il < left. length && ir < right. length ){
if (left[il] < right[ir]){
result. push (left[il ++ ]);
} else {
result. push (right[ir ++ ]);
}
}
return result. concat (left. slice (il)). concat (right. slice (ir));
}
上面的 merge 函数,合并两个已经按升序排好序的数组 。首先,比较两个数组的第一个元素 ,将其中较小 的一个放入 result 数组;然后,将其中较大 的一个与另一个数组的第二个元素 进行比较,再将其中较小的一个放入 result 数组的第二个位置。以此类推,直到一个数组的所有元素都进入 result 数组为止,再将另一个数组剩下的元素接着 result 数组后面返回(使用 concat 方法)。
有了 merge 函数,就可以对任意数组排序了。基本方法是将数组不断地拆成两半,直到每一半只包含零个元素或一个元素为止,然后就用 merge 函数,将拆成两半的数组不断合并 ,直到合并成一整个排序完成的数组。
function mergeSort ( myArray ){
if (myArray. length < 2 ) {
return myArray;
}
var middle = Math. floor (myArray. length / 2 ),
left = myArray. slice ( 0 , middle),
right = myArray. slice (middle);
return merge ( mergeSort (left), mergeSort (right));
}
上面的代码有一个问题,就是返回的是一个全新的数组,会多占用空间 。因此,修改上面的函数,使之在原地排序 ,不多占用空间。
function mergeSort ( myArray ){
if (myArray. length < 2 ) {
return myArray;
}
var middle = Math. floor (myArray. length / 2 ),
left = myArray. slice ( 0 , middle),
right = myArray. slice (middle),
params = merge ( mergeSort (left), mergeSort (right));
// 在返回的数组头部,添加两个元素,第一个是0,第二个是返回的数组长度
params. unshift ( 0 , myArray. length );
// splice用来替换数组元素,它接受多个参数,
// 第一个是开始替换的位置,第二个是需要替换的个数,后面就是所有新加入的元素。
// 因为splice不接受数组作为参数,所以采用apply的写法。
// 这一句的意思就是原来的myArray数组替换成排序后的myArray
myArray.splice. apply (myArray, params);
// 返回排序后的数组
return myArray;
}
ES6 实现
/**
* 归并排序:先分后合
* @param {*} arr
* @returns
*/
function mergeSort ( arr ) {
if (arr. length < 2 ) return arr;
let midddle = parseInt (arr. length / 2 );
const left = arr. slice ( 0 , midddle);
const right = arr. slice (midddle);
return merge ( mergeSort (left), mergeSort (right));
}
const merge = ( left , right ) => {
let result = [];
//两两比较数组开头元素
while (left. length && right. length ) {
left[ 0 ] <= right[ 0 ]
? result. push (left. shift ())
: result. push (right. shift ());
}
// 将剩余元素放入结果中
while (left. length ) result. push (left. shift ());
while (right. length ) result. push (right. shift ());
return result;
};
测试代码
let arr = [ 1 , 5 , 4 , 3 , 6 , 8 ];
console. log ( "原数组" , arr);
arr3 = mergeSort (arr);
console. log ( "归并排序:" , arr);
扩展阅读