討厭算法的程序員 6 - 歸并排序

討厭算法的程序員系列入口

分而治之

分而治之

從算法設計的分類上來說,插入排序屬于增量方法。在排序好子數組A[1 ‥ j-1]后,再將單個元素A[j]插入子數組的適當位置,產生排序好的子數組A[1 ‥ j]。整個算法就是不斷以此方法增量插入,直到子數組包含了所有數組元素。

本篇將要介紹的歸并排序,是用另一種思想來解決排序問題的,在算法設計分類上屬于分治法

分治法思想是,將原問題分解為幾個規模較小但類似于原問題的子問題,遞歸的求解這些子問題,然后在合并這些子問題的解,最終建立原問題的解。

這里提到一個詞遞歸,其解釋是:為了解決一個給定問題,算法一次或多次的調用其自身以解決緊密相關的子問題。遞歸是分治思想的一個具體實現。

分治模式在每層遞歸時都有三個步驟:

  • 分解:將原問題分解為若干子問題,這些子問題是原問題的規模較小的實例;
  • 解決:遞歸的求解各子問題;
  • 合并:合并子問題的解,得到原問題的解。

看到這里,“直覺”上可能會產生一個極大的疑問:最底層的子問題是在哪里解決的?產生這個疑問是正常的,因為第二步“解決”也僅僅是調用自身,其實就是重新進入了下一層的分解、解決和合并,而沒有看到“如何解決”。

答案是:無需解決。換句話說,層層分解到子問題的規模足夠小時,解就自己出現了。后面還會再提到這一點。

歸并排序偽碼

歸并排序按照分治法的三個步驟如下:

  • 分解:分解待排序的n個元素的序列,變成各具n/2個元素的兩個子序列;
  • 解決:遞歸的調用自身排序兩個子序列;
  • 合并:合并兩個已排序的子序列以產生最終排序的序列。

上一篇合并算法中已經解決了合并算法MERGE,歸并排序就剩下如何進行分解,和遞歸調用了。

看代碼的確就這三步:

MERGE-SORT(A, p, r)
1 if p < r
2   q = (p + r) / 2
3   MERGE-SORT(A, p, q)
4   MERGE-SORT(A, q+1, r)
5   MERGE(A, p, q, r)

注:(p + r) / 2如果不是整除,則取小于它的最大整數。

p < r時,表明數組有繼續拆分的可能。當p ≥ r時,則表示該子數組最多有一個元素,所以無需排序就已經是排好序了,這就是分解到足夠小會導致的自動解決。換句話說,我們一直把數組分解下去,直到分成每個子數組只包含1個元素時,即第3行中p = q,第4行中q+1 = r,那么第3和第4行的MERGE-SORT會立即返回,并執行MERGE,然后返回上一層MERGE-SORT,直到最上層。

一個例子

一個有8個元素的數組A[5, 2, 4, 7, 1, 3, 2, 6],采用歸并排序的圖示如下圖。圖中的下方藍區部分是上面白區的數組不同時刻的鏡像。白區主要在做“分解”,藍區主要在做“合并”。

歸并排序

歸并排序Java代碼

public static void mergeSortInASC(int [] numbers, int p, int r) throws Exception {
    if(p < r){
        int q = (int)Math.floor((p + r) / 2);
        mergeSortInASC(numbers, p, q);
        mergeSortInASC(numbers, q + 1, r);
        mergeInASC(numbers, p, q, r);
    }
}

MergeSort.java下載

上一篇 5 合并算法
下一篇 7 歸并排序的時間復雜度分析


共享協議:署名-非商業性使用-禁止演繹(CC BY-NC-ND 3.0 CN)
轉載請注明:作者黑猿大叔(簡書)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容