排序算法之歸併排序

這裏是傳送門⇒總結:關於排序算法html



平均時間複雜度 最優時間複雜度 最差時間複雜度 空間複雜度 穩定性
二路歸併排序 O(nlogn) O(nlogn) O(nlogn) O(n) 穩定


歸併排序採用分治策略,先劃分,再合併。若排序中每次合併的時候是2個有序子序列合併,稱「二路歸併排序」。這裏討論的就是「二路歸併排序」的遞歸作法(好像遞歸可能爆棧,若是能夠,建議研究下非遞歸作法)算法

  • 算法描述
    • 將待排序列分紅2部分,分別使2個子序列有序,以後把2個有序子序列合併成更大的有序序列
    • 而使2個子序列有序的方法是把子序列再分紅2部分,再分別使2個子序列有序,再合併
    • 而使2個子序列...
    • 直到子序列只有1個元素
  • 合併的具體實現
    • 設置2個指針分別從左到右掃描2個有序子序列,依次尋找2個子序列較小元素放入temp數組,用temp中合併排序好的子序列替換原數組array中沒排序好的原子序列
  • JS實現
// 此處傳入的array會被直接改變
function Merge(array, left, mid, right) {
    var i = left;
    var j = mid + 1;
    var k = 0;
    var temp = [];
    while (i <= mid && j <= right) {
        if (array[i] <= array[j]) {
            temp[k++] = array[i++];
        } else {
            temp[k++] = array[j++];
        }
    }
    while (i <= mid) {
        temp[k++] = array[i++];
    }
    while (j <= right) {
        temp[k++] = array[j++];
    }
    while (k > 0) {
        array[--j] = temp[--k];
    }
}

// 此處傳入的array會被直接改變
function MergeSort(array, left, right) {
    if (left < right) {
        var mid = Math.floor((left + right) / 2);
        MergeSort(array, left, mid);
        MergeSort(array, mid + 1, right);
        Merge(array, left, mid, right);
    }
}
  • 分析
    • 歸併排序過程當中,須要不斷對當前序列進行對半劃分,直到子序列長度爲1,也就是說每一次劃分後序列長度都爲本來的1/2。這意味着須要劃分log2n次(由n/2k = 1計算可得次數k)
    • 每一次劃分以後都會進行合併操做,每一次劃分後的合併操做會將長度爲n的待排序列遍歷一遍。過程以下:在第1次劃分中,出現2個長度爲n/2的子序列,須要合併1次,每次遍歷n個數據;在第2次劃分中,出現4個長度爲n/4的子序列,須要合併2次,每次遍歷n/2個數據;...當第log2n次劃分中,出現n個長度爲1的子序列,須要合併n/2次,每次遍歷2個數據。經過這個過程能夠知道每一層(劃分+合併)的時間複雜度爲O(n)
    • 每一層的時間複雜度爲O(n),結合上面分析的劃分次數爲log2n,能夠獲得歸併排序的時間複雜度爲O(nlogn)
    • 在歸併排序的Merge函數中,合併2個子序列時,須要藉助額外的存儲空間。每次合併過程當中都須要申請額外的內存空間,可是合併完成後,臨時開闢的內存空間就被釋放掉了,在任意時刻,只會有一個Merge函數在執行,也就只會有一個臨時的內存空間在使用。臨時空間再大也不會超過n個數據的大小,因此空間複雜度是O(n)
    • 因爲在合併時,若是有兩個相同的元素,是先把左邊子序列中的元素放進temp,再放右邊子序列的相同元素,因此兩個相同元素的相對位置不變,即歸併排序是穩定的
相關文章
相關標籤/搜索