【C/C++】排序算法之归并排序

归并排序

文章目录

  • 归并排序
  • 一、归并排序是什么?
  • 二、基本实现
    • 1.合并(治)
    • 2.排序(分)
    • 3.完整代码


一、归并排序是什么?

归并排序(Merge Sort)是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

时间复杂度 O(n log n)
空间复杂度 T(n)

简而言之,归并排序是将大问题拆分成几个小问题进行解决(分)再将解决后的小问题合并起来(治)最终解决了整个复杂的问题
【C/C++】排序算法之归并排序_第1张图片

二、基本实现

核心步骤:

  1. 拆分
  2. 合并

1.合并(治)

代码解析:

在这部分函数里,我们将已经排好序的各部分序列进行合并,此时可以想象,有两个已经排好序的序列A和B,要将A和B进行合并,但是问题是,A和B之间并没有排好序,换句话说,我们还要将A和B合并在一起的序列再排个序。
要实现A和B之间排序,我们就需要一个临时数组,以存放正确序列
首先,以mid为中心,A序列以low为起点,mid为终点;B序列以mid+1为起点,high为终点。

之后,将A,B序列中的数字进行比较,小的放在临时数组里,这样不断比较下来,临时数组就存放了正确的从小到大排列的序列。

最后,如果A或者B序列中仍有残留的数字,则之间将这些数字接到临时数组的末端即可。然后将临时数组中的数字放到起始数组中,此时,我们一开始传入的数组就已经排好序了。
void merge(int *a,int low,int mid,int high)
{
     
    int *tmp=(int*)malloc((high-low+1)*sizeof(int));//开辟一个临时数组
    int k=0;
    int i=low;
    int j=mid+1;
    while(i<=mid&&j<=high)
    {
     
        if(a[i]<a[j]) tmp[k++]=a[i++];
        else tmp[k++]=a[j++];
    }
    if(i==mid+1)
        while(j<=high)
            tmp[k++]=a[j++];
    if(j==high+1)
        while(i<=mid)
            tmp[k++]=a[i++];
    for(j=0,i=low;j<k;i++,j++)
    a[i]=tmp[j];
    return;
}

2.排序(分)

代码解析:

这部分比较精简,我们需要取序列的中间值mid,之后再将序列进行分割,这部分用递归完成,直到子序列只剩下一个数字。

这部分依旧可以想象成一个序列分割成A,B两个子序列,之后A再分成A1,A2这两个子序列,B分割成B1,B2这两个子序列,以此类推,不断地将子序列进行分割,直到子序列只剩下一个数字。

最后,我们调用上面写的合并函数进行归并即可。
void merge_sort(int *a,int low,int high)
{
     
    if(low>=high) return;
    int mid=(low+high)/2;
    merge_sort(a,low,mid);
    merge_sort(a,mid+1,high);
    merge(a,low,mid,high);
}

【C/C++】排序算法之归并排序_第2张图片

3.完整代码

#include
#include 
int a[101];
int n;
void merge(int *a,int low,int mid,int high)
{
     
    int *tmp=(int*)malloc((high-low+1)*sizeof(int));
    int k=0;
    int i=low;
    int j=mid+1;
    while(i<=mid&&j<=high)
    {
     
        if(a[i]<a[j]) tmp[k++]=a[i++];
        else tmp[k++]=a[j++];
    }
    if(i==mid+1)
        while(j<=high)
            tmp[k++]=a[j++];
    if(j==high+1)
        while(i<=mid)
            tmp[k++]=a[i++];
    for(j=0,i=low;j<k;i++,j++)
    a[i]=tmp[j];
    return;
}
void merge_sort(int *a,int low,int high)
{
     
    if(low>=high) return;
    int mid=(low+high)/2;
    merge_sort(a,low,mid);
    merge_sort(a,mid+1,high);
    merge(a,low,mid,high);
}
int main()
{
     
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    scanf("%d",a+i);
    merge_sort(a, 0, n-1);
    for(int i=0;i<n;i++)
    printf("%d ",a[i]);
}

C++版本:

#include 
//#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){
     

    //* VS不支持动态长度数组, 即不能使用 T aux[r-l+1]的方式申请aux的空间
    //* 使用VS的同学, 请使用new的方式申请aux空间
    //* 使用new申请空间, 不要忘了在__merge函数的最后, delete掉申请的空间:)
    T aux[r-l+1];
    //T *aux = new T[r-l+1];

    for( int i = l ; i <= r; i ++ )
        aux[i-l] = arr[i];

    // 初始化,i指向左半部分的起始索引位置l;j指向右半部分起始索引位置mid+1
    int i = l, j = mid+1;
    for( int k = l ; k <= r; k ++ ){
     

        if( i > mid ){
       // 如果左半部分元素已经全部处理完毕
            arr[k] = aux[j-l]; j ++;
        }
        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 ++;
        }
    }

    //delete[] aux;
}

// 递归使用归并排序,对arr[l...r]的范围进行排序
template<typename T>
void __mergeSort(T arr[], int l, int r){
     

    if( l >= r )
        return;

    int mid = (l+r)/2;
    __mergeSort(arr, l, mid);
    __mergeSort(arr, mid+1, r);
    __merge(arr, l, mid, r);
}

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

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


// 比较InsertionSort和MergeSort两种排序算法的性能效率
// 整体而言, MergeSort的性能最优, 对于近乎有序的数组的特殊情况, 见测试2的详细注释
int main() {
     

    // Merge Sort是我们学习的第一个O(nlogn)复杂度的算法
    // 可以在1秒之内轻松处理100万数量级的数据
    // 注意:不要轻易尝试使用SelectionSort, InsertionSort或者BubbleSort处理100万级的数据
    // 否则,你就见识了O(n^2)的算法和O(nlogn)算法的本质差异:)
    int n = 50000;

    // 测试1 一般性测试
    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;

    cout<<endl;


    // 测试2 测试近乎有序的数组
    // 对于近乎有序的数组, 数组越有序, InsertionSort的时间性能越趋近于O(n)
    // 所以可以尝试, 当swapTimes比较大时, MergeSort更快
    // 但是当swapTimes小到一定程度, InsertionSort变得比MergeSort快
    int swapTimes = 10;
    assert( swapTimes >= 0 );

    cout<<"Test for nearly ordered array, size = "<<n<<", swap time = "<<swapTimes<<endl;
    arr1 = SortTestHelper::generateNearlyOrderedArray(n,swapTimes);
    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;
}

你可能感兴趣的:(题解,基础知识,c++,c语言)