1. 程式人生 > >排序演算法之歸併排序MergeSort

排序演算法之歸併排序MergeSort

排序演算法之歸併排序

歸併排序的主要思想為:分治法 即將問題分解為本質相同的若干個分問題,通過對分問題的求解,達到對總問題求解的目的。中間會用到程式設計中的一個重要思想—遞迴思想。

現在假設給定一個無序的長度為n的陣列

  • 我們可以取陣列的中間值mid
  • 然後我們可以得到兩個陣列,那麼相似的,我們也可以把對這兩個陣列排序的問題看做同對原陣列排序一樣的分問題。
  • 假定我們已經利用遞迴思想對上述兩個陣列完成了排序,那麼我們只需要合併兩個陣列便可以完成對原陣列的排序了。
  • 接著,我們可以把這兩個陣列理解為整理好的兩堆書,為了合併為一堆有序的書,我們把兩堆書都有序的擺放。
  • 一次比較放在兩堆書上最上邊的那本書,將排序靠前的書拿出來。
  • 重複上一步驟直到其中一堆書被拿完,剩下的書再依次的拿出來。(在實際操作中,我們也可以採用另一種思路,即:在兩堆書的最下邊放上一本標記書(我們稱為訊號量),所有其他的書與標記書比較時都排在前面。這樣我們便不必統計什麼時候其中的一堆書被拿完了,只需考慮從兩堆書中拿出n本書即可完成排序,因為最後肯定會剩下兩本標記書)

具體過程見下圖: MergeSort

程式碼展示: cpp 版

#include <iostream>
using namespace std;
const int INF=0x3f3f3f3f;	//定義一個“大數”
// 排序的部分開始為head,結尾為tail,中間值為mid
// 用來合併兩組數的Merge函式
void merge(int a[], int head, int mid, int tail) { int n1 = mid - head + 2; int n2 = tail - mid + 1; // 將兩部分取出來存在新陣列中 int B[n1]; int C[n2]; for (int i=0; i<n1-1; i++) { B[i] = a[i + head]; } for (int i=0; i<n2-1; i++) { C[i] = a[i + 1 + mid]; } // 儲存訊號量
B[n1 - 1] = INF; C[n2 - 1] = INF; int i = 0, j = 0; for (int k=head; k!=tail+1; k++) { if (B[i] <= C[j]) { a[k] = B[i++]; } else { a[k] = C[j++]; } } } // 排序函式mergeSort void mergeSort(int a[], int head, int tail) { if (head < tail) { // 取中間值 int mid = (head + tail) / 2; // 遞迴分解head到mid的資料 mergeSort(a, head, mid); // 遞迴分解mid+1到end的資料 mergeSort(a, mid + 1, tail); // 合併兩組排好序的資料 merge(a, head, mid, tail); } } int main() { int a[] = {1, 30, 20, 34, 78, 99, 40, 2, 7, 16}; // 注意index不要越界 mergeSort(a, 0, 9); int i = 0; for (i=0; i<10; i++) { cout << a[i] << " "; } cout << endl; return 0; }
output:
1 2 7 16 20 30 34 40 78 99 

java 版

// 定義MergeSort類
public class MergeSort {
    // 由遞迴思想我們認為,我們要對從p到r的陣列片段進行排序
    // p即為head,r即為tail,q即為mid
    // 用來合併兩組數的Merge函式
    public static void merge(int[] a, int p, int q, int r) {
        int n1 = q - p + 2;
        int n2 = r - q + 1;
        // 將兩組數分別另存起來
        int[] B = new int[n1];
        int[] C = new int[n2];
        // 這裡的a為原陣列的地址,而我們只操作原陣列上的一部分
        // 即從p到q,從q+1到r,注意index的值
        for (int i=0; i<n1-1; i++) {
            B[i] = a[i + p];
        }
        for (int i=0; i<n2-1; i++) {
            C[i] = a[i + q + 1];
        }
        // 放入訊號量
        B[n1 - 1] = (int)Double.POSITIVE_INFINITY;
        C[n2 - 1] = (int)Double.POSITIVE_INFINITY;
        int i = 0, j = 0;
        for (int k=p; k!=r+1; k++) {
            if (B[i] <= C[j]) {
                a[k] = B[i];
                i++;
            }
            else {
                a[k] = C[j];
                j++;
            }
        }
    }
    // 排序函式mergeSort
    public static void mergeSort(int[] a, int p, int r) {
        if (p < r) {
            // 取中間值
            int q = (p + r) / 2;
            // 遞迴分解p到q-1的資料
            mergeSort(a, p, q);
            // 遞迴分解q到r的資料
            mergeSort(a, q + 1, r);
            // 合併兩組排好序的資料
            merge(a, p, q, r);
        }
    }
    public static void main(String[] args) {
        int[] a = {1, 30, 20, 34, 78, 99, 40, 2, 7, 16};
        // 注意index不要越界
        mergeSort(a, 0, a.length - 1);
        for (int i=0; i<a.length; i++) {
            System.out.printf("%d ", a[i]);
        }
    }
}
output
1 2 7 16 20 30 34 40 78 99 

最後推薦給大家一個可以視覺化資料結構與演算法的網站:VisuAlgo 如果你也喜歡這篇文章,請點個贊哦~