归并排序法-Merge Sort

归并排序-Merge Sort(O(nlogn))

【算法思路】
(1)要对如下数组进行归并排序,首先将数组分为两半分别进行排序。
在这里插入图片描述
在这里插入图片描述
(2)然后再将左边数组和右边数组分别分为两半,进行排序。
在这里插入图片描述
(3)同理,再次分半。这是每个部分只有一个元素,可以当作是有序的,这是我们要向上归并。
在这里插入图片描述

在这里插入图片描述(4)一直归并,直到归并到最后一层,这是数组就已经有序了
在这里插入图片描述
在这里插入图片描述
【归并过程】
(1)首先开辟一个同样大小的临时空间来辅助进行归并的过程。虽然算法时间复杂度相比于插入排序和选择排序提升到了O(nlongn),但是多使用了存储空间。
在这里插入图片描述
(2)需要三个索引来辅助归并过程。其中蓝色箭头代表最终在归并过程中需要跟踪的位置,两个红色箭头指向两个排好序的数组当前考虑的元素。首先我们考虑1和2谁应该先放入到最终的数组中。很明显1比2小,1先放入最终数组。然后蓝色箭头后移,考虑下一个位置应该放入的元素,相应的,1所在数组红色箭头也要后移。
在这里插入图片描述
在这里插入图片描述
(3)同理,2比4小,将2放入最终数组。以此类推。
在这里插入图片描述
【代码】
在这里插入图片描述
<merge_sort.cpp>

#include <iostream>
#include <algorithm>
#include "SortTestHelper.h"
#include "InsertionSort.h"
using namespace std;
//将arr[l...mid]和arr[mid+1...r]两部分进行归并
template <typename T>
void __merge(T arr[], int l, int mid, int r) {
    //辅助空间
    T aux[r - l + 1];
    for(int i = l; i <= r; i++) {
        aux[i - l] = arr[i]; //l为偏移量
    }
    int i = l, j = mid + 1;
    for(int k = l; k <= r; k++) {
        if(i > mid) {
            arr[k] = aux[j - l];
        }
        else if(j > r) {
            arr[k] = aux[i - l];
            i++;
        }
        else if(aux[i - l] < aux[j - l]) {
            arr[k] = aux[i - l];
            i++;
        }
        else {
            arr[k] = aux[j - l];
            j++;
        }
    }
}
//递归使用归并排序,对arr[l...r]的范围进行排序
template <typename T>
void __mergeSort(T arr[], int l, int r) {
    /*
    if(l >= r)
    	return;
    */
	//引入插入排序改进归并排序,
    if(r - l <= 15) {
        insertionSort(arr, l, r);
        return;
    }
    int mid = (l + r) / 2;
    __mergeSort(arr, l, mid);
    __mergeSort(arr, mid + 1, r);
    //当两个有序数组连在一起无序时才需要合并
    if(arr[mid] > arr[mid + 1])
        __merge(arr, l, mid, r);
}
template <typename T>
void mergeSort(T arr[], int n) {

    __mergeSort(arr, 0, n - 1);
}

int main() {
    int n = 50000;
    cout << "Test for Random Array, size = " << n << ", random range [0, " << n << "]" << endl;
    int *arr1 = SortTestHelper::generateRandomArray(n, 0, n);
    int *arr2 = SortTestHelper::copyIntArray(arr1, n);
    SortTestHelper::testSort("Insertion Sort", insertionSort, arr1, n);
    SortTestHelper::testSort("Merge Sort", mergeSort, arr2, n);
    delete[] arr1;
    delete[] arr2;
    return 0;
}

<insertion_sort.h>

#ifndef SORTALGORITHM_INSERTIONSORT_H
#define SORTALGORITHM_INSERTIONSORT_H
#include <iostream>
#include <algorithm>
#include "SortTestHelper.h"
#include "selectionsort.h"
using namespace std;
//对arr[l...r]范围的数组进行插入排序
template <typename T>
void insertionSort(T arr[], int l, int r) {
    for(int i = l + 1; i <= r; i++) {
        T e = arr[i];
        int j;
        for(j = i; j > l && arr[j - 1] > e; j--)
            arr[j] = arr[j - 1];
        arr[j] = e;
    }
    return;
}
//比起选择排序,插入排序可以提前终止内层循环,提高效率
template <typename T>
void insertionSort(T arr[], int n) {
    for(int i = 1; i < n; i++) {
        //寻找元素arr[i]合适的插入位置
        T e = arr[i];
        int j; //j保存元素e应该插入的位置
        for(j = i; j > 0 && arr[j-1] > e; j--) {
            arr[j] = arr[j-1];
        }
        arr[j] = e;
    }
    //swap函数需三次赋值,可以优化为一次赋值
    /*
    for(int i = 1; i < n; i++) {
        for(int j = i; j > 0 && arr[j] < arr[j-1]; j--) {
            swap(arr[j], arr[j-1]);
        }
    }
    */
}
#endif //SORTALGORITHM_INSERTIONSORT_H

<SortTestHelper.h>

#ifndef SELECTIONSORT_SORTTESTHELPER_H
#define SELECTIONSORT_SORTTESTHELPER_H

#include <iostream>
#include <ctime>
#include <cassert>
using namespace std;
namespace SortTestHelper {
    int* generateRandomArray(int n, int rangeL, int rangeR) {
        assert(rangeL <= rangeR);
        int *arr = new int[n];
        srand(time(NULL));
        for(int i = 0; i < n; i++) {
            arr[i] = rand() % (rangeR - rangeL + 1) + rangeL;
        }
        return arr;
    }
    template <typename T>
    void printArray(T arr[], int n) {
        for(int i = 0; i < n; i++) {
            cout << arr[i] << " ";
        }
        cout << endl;
        return;
    }
    template <typename T>
    bool isSorted(T arr[], int n) {
        for(int i = 0; i < n - 1; i++) {
            if(arr[i] > arr[i + 1])
                return false;
        }
        return true;
    }
    template <typename T>
    void testSort(string sortName, void(*sort)(T[], int), T arr[], int n) {
        clock_t startTime = clock();
        sort(arr, n);
        clock_t endTime = clock();
        assert(isSorted(arr, n));
        cout << sortName << " : " << double(endTime - startTime) / CLOCKS_PER_SEC << " s" << endl;
        return;
    }

    int* copyIntArray(int a[], int n) {
        int* arr = new int[n];
        copy(a, a+n, arr);
        return arr;
    }
    int *generateNearlyOrderedArray(int n, int swapTimes) {
        int *arr = new int[n];
        for(int i = 0; i < n; i++) {
            arr[i] = i;
        }
        srand(time(NULL));
        for(int i = 0; i < swapTimes; i++) {
            int posx = rand()%n;
            int posy = rand()%n;
            swap(arr[posx], arr[posy]);
        }
        return arr;
    }
}
#endif //SELECTIONSORT_SORTTESTHELPER_H

【自底向上归并排序】
前面所写的是自顶向下的归并排序算法,其实我们也可以按照自底向上的方式进行归并排序。
(1)首先我们可以两个元素为一个小段进行归并排序。
在这里插入图片描述
在这里插入图片描述
(2)然后4个元素为一个小段进行归并排序的过程。
在这里插入图片描述
在这里插入图片描述
(3)最后8个小段进行归并排序,也就是整个数组完成归并排序的过程。在整个过程中不需要递归,只需要迭代就能完成整个过程。
在这里插入图片描述
【代码】

#include <iostream>
#include <algorithm>
#include "SortTestHelper.h"
#include "MergeSort.h"
using namespace std;
//自底向上实现归并排序算法
template <typename T>
void mergeSortBU(T arr[], int n) {
    for(int sz = 1; sz <= n; sz += sz) {
        for(int i = 0; i + sz < n; i +=sz + sz) {
            //对arr[i...i+sz-1]和arr[i+sz...i+2*sz-1]进行归并
            __merge(arr, i, i + sz - 1, min(i + sz + sz - 1, n - 1));
        }
    }
}

int main() {
    int n = 50000;
    cout << "Test for Random Array, size = " << n << ", random range [0, " << n << "]" << endl;
    int *arr1 = SortTestHelper::generateRandomArray(n, 0, n);
    int *arr2 = SortTestHelper::copyIntArray(arr1, n);

    SortTestHelper::testSort("MergeBU Sort", mergeSortBU, arr1, n);
    SortTestHelper::testSort("Merge Sort", mergeSort, arr2, n);

    delete[] arr1;
    delete[] arr2;
    return 0;
}

<MergeSort.h>

#ifndef SORTALGORITHM_MERGESORT_H
#define SORTALGORITHM_MERGESORT_H
#include <iostream>
#include <algorithm>
#include "SortTestHelper.h"
#include "InsertionSort.h"
using namespace std;
//将arr[l...mid]和arr[mid+1...r]两部分进行归并
template <typename T>
void __merge(T arr[], int l, int mid, int r) {
    T aux[r - l + 1];
    for(int i = l; i <= r; i++) {
        aux[i - l] = arr[i]; //辅助数组
    }
    int i = l, j = mid + 1;
    for(int k = l; k <= r; k++) {
        if(i > mid) {
            arr[k] = aux[j - l];
        }
        else if(j > r) {
            arr[k] = aux[i - l];
            i++;
        }
        else if(aux[i - l] < aux[j - l]) {
            arr[k] = aux[i - l];
            i++;
        }
        else {
            arr[k] = aux[j - l];
            j++;
        }
    }
}
//递归使用归并排序,对arr[l...r]的范围进行排序
template <typename T>
void __mergeSort(T arr[], int l, int r) {
//    if(l >= r)
//        return;
//引入插入排序改进归并排序
    if(r - l <= 15) {
        insertionSort(arr, l, r);
        return;
    }
    int mid = (l + r) / 2;
    __mergeSort(arr, l, mid);
    __mergeSort(arr, mid + 1, r);
    if(arr[mid] > arr[mid + 1])
        __merge(arr, l, mid, r);
}

template <typename T>
void mergeSort(T arr[], int n) {

    __mergeSort(arr, 0, n - 1);
}
#endif //SORTALGORITHM_MERGESORT_H

<SortTestHelper.h>

#ifndef SELECTIONSORT_SORTTESTHELPER_H
#define SELECTIONSORT_SORTTESTHELPER_H

#include <iostream>
#include <ctime>
#include <cassert>
using namespace std;
namespace SortTestHelper {
    int* generateRandomArray(int n, int rangeL, int rangeR) {
        assert(rangeL <= rangeR);
        int *arr = new int[n];
        srand(time(NULL));
        for(int i = 0; i < n; i++) {
            arr[i] = rand() % (rangeR - rangeL + 1) + rangeL;
        }
        return arr;
    }
    template <typename T>
    void printArray(T arr[], int n) {
        for(int i = 0; i < n; i++) {
            cout << arr[i] << " ";
        }
        cout << endl;
        return;
    }
    template <typename T>
    bool isSorted(T arr[], int n) {
        for(int i = 0; i < n - 1; i++) {
            if(arr[i] > arr[i + 1])
                return false;
        }
        return true;
    }

    template <typename T>
    void testSort(string sortName, void(*sort)(T[], int), T arr[], int n) {
        clock_t startTime = clock();
        sort(arr, n);
        clock_t endTime = clock();
        assert(isSorted(arr, n));
        cout << sortName << " : " << double(endTime - startTime) / CLOCKS_PER_SEC << " s" << endl;
        return;
    }

    int* copyIntArray(int a[], int n) {
        int* arr = new int[n];
        copy(a, a+n, arr);
        return arr;
    }

    int *generateNearlyOrderedArray(int n, int swapTimes) {
        int *arr = new int[n];
        for(int i = 0; i < n; i++) {
            arr[i] = i;
        }
        srand(time(NULL));
        for(int i = 0; i < swapTimes; i++) {
            int posx = rand()%n;
            int posy = rand()%n;
            swap(arr[posx], arr[posy]);
        }
        return arr;
    }
}
#endif //SELECTIONSORT_SORTTESTHELPER_H

【注】正常情况下自顶向下的归并排序要比自底向上的归并排序性能好。

发布了29 篇原创文章 · 获赞 11 · 访问量 3953

猜你喜欢

转载自blog.csdn.net/weixin_41462017/article/details/104696539
今日推荐