排序演算法——歸併排序

桃子夭夭發表於2015-10-12

思路

我學習的歸併是簡單的二路歸併,思路如下:

① 將陣列平均分成兩份

② 遞迴重複①,直到每個陣列中只有1個元素,只有一個元素的陣列可以認為是排好序的

③ 將兩個排好序的陣列合併成一個排好序的陣列

④ 重複③直到最終得到一個排好序的陣列

javascript實現

/**
 * 歸併排序
 * @param arr
 * @returns 
 */
function mergeSort(arr){
    if(!(arr instanceof  Array)){
        return [];
    }
    if(arr.length<=1){
        return arr;
    }

    var left=arr.splice(0,Math.floor(arr.length/2)),
        right=arr,
        res=[];
    left=mergeSort(left);
    right=mergeSort(right);
    //一次歸併,認為左右兩邊的陣列是排序好的,通過這種方式將它們合成為一個排序好的陣列
    //只有一個元素的陣列可以認為是已經排序好的,所以通過:
    //left=mergeSort(left);
    //right=mergeSort(right);
    //兩步遞迴得到拆分為一個元素的陣列,再通過下面的歸併得到結果
    while(left.length && right.length){
        left[0]>right[0] ? res.push(right.shift()): res.push(left.shift());
    }
    //歸併剩下的元素都是比res中的大的,直接拼接到後邊就好了
    res=res.concat(left,right);
    return res;
}

其他

在一些文章裡邊看到有人認為瀏覽器中,採用遞迴函式呼叫的方式實現演算法有棧溢位的風險。

因為函式每次呼叫都會產生一個上下文,放在上下文棧中,執行完畢後才會清除。但是遞迴呼叫會導致很多個上下文物件,這樣如果待排序的陣列過長,在某些瀏覽器下可能會發生棧溢位的錯誤。

測試瀏覽器上下文棧上限的程式碼:

var cnt = 0;
try {
  (function() {
    cnt++;
    arguments.callee();
  })();
} catch(e) {
  console.log(e.message, cnt);
}

我測試過後發現主流瀏覽器的上下文棧長度上限都能達到2w左右,在前端一般不會做如此大量資料的排序。

也可以選擇將遞迴的方式改為迭代的方式來規避這個問題

相關文章