MergeSort歸併排序遞歸、迭代、原地 c++實現

分治(Divide and Conquer)的思想,平均、最好、最壞,時間效率都是O(n log n)。
有遞歸和迭代兩種實現方法。對於順序存儲如數組,一般爲O(n)的空間;list的空間直接爲O(1)。
空間上可以使用原地歸併避免O(n)的空間。但較麻煩,且需要多次移動,除非有特殊說明或空間非常寶貴,否則不建議原地歸併。

void merge_sort(vector<int>& arr){
    if(arr.empty()) return;
    vector<int> ans(arr.size());
    merge_sort_re(arr, ans, 0, arr.size());//1.遞歸
    //merge_sort_it(arr, arr.size());//2.迭代
}

1.遞歸

void merge_sort_re(vector<int>& arr, vector<int>& ans, int beg, int end){
    if(end - beg <= 1)return;
    int mid = beg + (end-beg)/2;
    merge_sort_re(arr, ans, beg, mid);
    merge_sort_re(arr, ans, mid, end);
    merge(arr, ans, beg, end);//a.歸併, O(n)的空間
    //merge_inplace(arr, beg, end);//b.原地歸併, O(1)的空間
}

a.歸併, O(n)的空間

void merge(vector<int>& arr, vector<int>& ans, int beg, int end){
    int mid = beg + (end-beg)/2;
    int i = beg, j = mid, k = beg;
    while(i<mid && j<end) ans[k++] = arr[i]>arr[j] ? arr[j++] : arr[i++];
    while(i<mid) ans[k++] = arr[i++];
    while(j<end) ans[k++] = arr[j++];
    for(k = beg; k<end; ++k) arr[k] = ans[k];

}

b. 原地歸併, O(1)的空間

merge_inplace(arr, beg, end),歸併[beg,end)範圍內的數。其中,[beg,mid)、[mid,end)分別有序。
算法流程:
(0)變量i、j初值分別爲beg、mid = beg + (end-beg)/2。
(1)首先自增i,在arr前半部找到第一個比arr[j]大的數,爲arr[i];
中間變量k保存此時j的值;
(2)然後自增j,在arr後半部找到第一個比arr[i]大的數,爲arr[j];
(3)此時,arr[i]前的數不需要處理;arr[x] (x的範圍爲[i,k) )中的全部數(記爲A_x)大於arr[y](y的範圍爲[k,j) )中的全部數(記爲A_y);記移動前,t = arr[j],現在只需要通過 move(arr, i, k, j) 將A_y 中的全部數移至A_x中的全部數前,即可保證arr在[beg, end)範圍內,移動後數t所在的位置之前(不包括數t所在的位置)的全部數都有序;
(3)更新i的值爲移動後arr[i]所在位置,即i自增區間[k,j)的長度 i+=j-k,重新執行(1)~(3),直到i、j滿足循環退出條件。

時間分析:最壞O(n^2),因此除非有特殊說明或空間非常寶貴,否則不建議原地歸併。

//將arr中[k,j)範圍內的數移至[i,k)範圍前
void move(vector<int>& arr, int i, int k, int j){
    reverse(arr.begin()+i, arr.begin()+k);
    reverse(arr.begin()+k, arr.begin()+j);
    reverse(arr.begin()+i, arr.begin()+j);
}
//原地歸併, O(1)的空間
void merge_inplace(vector<int>& arr, int beg, int end){
    int mid = beg + (end-beg)/2;
    int i = beg, j = mid;
    while(i<j && j<end){
        int k = j;
        while(i<j && arr[i]<=arr[j]) ++i;
        while(j<end && arr[j]<=arr[i]) ++j;
        move(arr, i, k, j);
        i+=j-k;
    }
}

reference: blog

2.迭代

void merge_sort_it(vector<int>& arr, int len){
    vector<int> ans(len);
    for(int seg = 1; seg < len; seg+=seg){
        for(int beg = 0; beg < len; beg+=seg+seg){
            int low = beg, mid = std::min(beg+seg, len), high = std::min(beg+seg+seg, len);
            int i = low, j = mid, k = low;
            while(i<mid && j < high) ans[k++] = arr[i]>arr[j] ? arr[j++] : arr[i++];
            while(i<mid) ans[k++] = arr[i++];
            while(j<high) ans[k++] = arr[j++];
            for(k = beg; k < high; ++k) arr[k] = ans[k];
        }
    }
}

reference: wiki


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章