1. 程式人生 > >歸併排序 及 C++實現

歸併排序 及 C++實現

歸併排序:

               時間複雜度:O(nlogn)

    優點:效率高、穩定

    缺點:佔用記憶體較多

歸併排序是建立在歸併操作上的一種有效的排序演算法,該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱為二路歸併。

歸併過程為:比較a[i]和a[j]的大小,若a[i]≤a[j],則將第一個有序表中的元素a[i]複製到r[k]中,並令i和k分別加上1;否則將第二個有序表中的元素a[j]複製到r[k]中,並令j和k分別加上1,如此迴圈下去,直到其中一個有序表取完,然後再將另一個有序表中剩餘的元素複製到r中從下標k到下標t的單元。歸併排序的演算法我們通常用遞迴實現,先把待排序區間[s,t]以中點二分,接著把左邊子區間排序,再把右邊子區間排序,最後把左區間和右區間用一次歸併操作合併成有序的區間[s,t]。

以上內容來自百度百科

歸併排序主要分為兩部分:

1、劃分子區間

2、合併子區間

現在以 9,6,7,22,20,33,16,20 為例講解上面兩個過程:

第一步,劃分子區間:每次遞迴的從中間把資料劃分為左區間和右區間。原始區間為[start,end],start=0,end=[length-1],減一是因為陣列的下標從0開始,本例中length=8,end=7.現在從中間元素劃分,劃分之後的左右區間分別為 [start,(end-start+1)/2+start],右區間為[(end-start+1)/2+start+1,end],本例中把start和end帶入可以得到[0,7],劃分後的左右子區間為[0,4],[5,7],然後分別對[start,end]=[0,4]和[start,end]=[5,7]重複上一步過程,直到每個子區間只有一個或者兩個元素。整個分解過程為:

子區間劃分好以後,分別對左右子區間進行排序,排好序之後,在遞迴的把左右子區間進行合併,整個過程如下圖所示:

程式碼如下:

歸併函式:

void merge_sort(vector<int> &data, int start, int end, vector<int> &result) {
    if (end - start == 1) {     // 如果區間只有兩個元素,則對這兩個元素進行排序
        if (data[start] > data[end]) {
            int temp = data[start];
            data[start] = data[end];
            data[end] = temp;
        }
        return;
    }
    else if (end - start == 0)  // 如果區間只有一個元素,則不用排序
        return;
    else {
        // 繼續劃分子區間,分別對左右子區間進行排序
        merge_sort(data, start, (end-start+1)/2+start, result);
        merge_sort(data, (end-start+1)/2+start+1, end, result);
        // 開始歸併已經排好序的start到end之間的資料
        merge(data, start, end, result);
        // 把排序後的區間資料複製到原始資料中去
        for (int i = start; i <= end; i++) {
            data[i] = result[i];
        }
    }
}

void merge(vector<int> &data, int start, int end, vector<int> &result) {
    int left_length = (end - start + 1) / 2 + 1;    // 左部分割槽間的資料元素個數
    int left_index = start;
    int right_index = start + left_length;
    int result_index = start;
    while ((left_index < start + left_length) && (right_index < end + 1)) {
        // 對分別已經排好序的左區間和右區間進行合併
        if (data[left_index] <= data[right_index]) {
            result[result_index++] = data[left_index++];
        }
        else
            result[result_index++] = data[right_index++];
    }
    while (left_index < start + left_length) {
        result[result_index++] = data[left_index++];
    }
    while (right_index < end + 1) {
        result[result_index++] = data[right_index++];
    }
}

測試主函式:

#include <iostream>
#include <vector>
using namespace std;

void merge_sort(vector<int> &data, int start, int end, vector<int> &result);
void merge(vector<int> &data, int start, int end, vector<int> &result);

int main() {
    vector<int> data ={9, 6, 7, 22, 20, 33, 16, 20};
    const int length = 8;
    vector<int> result(length);
    cout << "Before sorted: " << endl;
    
    for (int i = 0; i < length; i++) {
        cout << data[i] << "   ";
    }
    cout << endl;
    
    cout << "After sorted: " << endl;
    merge_sort(data, 0, length-1, result);
    for (int i = 0; i < length; i++) {
        cout << data[i] << "   ";
    }
    cout << endl;
    
    return 0;
}