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


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