数据结构与算法C++之归并排序

本文详细介绍了归并排序的原理和C++实现,通过比较归并排序与选择排序、插入排序的性能差异,展示其O(nlogn)的时间复杂度优势。归并排序采用分治策略,递归地将数组切分并合并成有序序列。在近乎有序的数组中,插入排序的表现可能优于归并排序。
摘要由CSDN通过智能技术生成

上两篇博客使用的选择排序和插入排序的算法复杂度都是O(n2),这在数组元素比较多的时候和一些算法复杂度为O(nlogn)的快速排序算法相比,差距还是很明显的,如下图
在这里插入图片描述
当有10万个元素的时候,快速排序算法比普通的选择排序算法要快了6000倍,
本篇博客介绍的是快速排序算法中的归并排序
归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分成一些小的问题然后递归求解,而治的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
在这里插入图片描述
可以看到这种结构很像一棵完全二叉树,本文的归并排序采用递归去实现,递归深度为logn。
分阶段就是将数组不断进行对半切分,直到最后每部分只剩下一个元素为止,此时只有一个元素我们就认为该部分是有序的。
治阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。
在这里插入图片描述
合并阶段需要在空间里另外开辟一个和数组相同大小的空间来存放排好序的数组,为上图的temp
定义指向temp的索引为k,指向左边序列的索引为i,指向右边序列的索引为j,左边序列最后一个元素的索引为mid
采用递归方法实现归并排序:

#include <iostream>

#ifndef _SORTINGHELP_H_
#define _SORTINGHELP_H_
#include "SortingHelp.h"
#endif // _SORTINGHELP_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];
            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++;
        }
    }
}

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

    int mid = (l + r)/2;
    __mergeSorting(arr, l, mid);
    __mergeSorting(arr, mid+1, r);
    if (arr[mid] < arr[mid+1])//如果左边的序列已经小于右边的序列,就不用合并了
        __merge(arr, l, mid, r);

}

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

    __mergeSorting(arr, 0, n-1);

}
int main()
{
    int n = 50000;
    int *arr = generateRandomArray(n, 0, n);
    int *arr2 = copyIntArray(arr, n);
    testSorting("InsertionSortingImproved", InsertionSortingImproved, arr2, n);
    testSorting("MergeSorting", MergeSorting, arr2, n);
    delete[] arr;//最后删除数组开辟的空间
    delete[] arr2;
    return 0;
}

SortingHelp.h定义为

#include <iostream>
#include <ctime>  //time()函数
#include <cstdlib> //rand()函数
#include <cassert> //assert()函数


using namespace std;

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;
}

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;
}


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 testSorting(string sortName, void(*sorting)(T[], int), T arr[], int n){
//第二个参数是传入排序函数的指针

    clock_t startClock = clock();
    sorting(arr, n);
    clock_t endClock = clock();
    assert(isSorted(arr, n));
    cout<<sortName<<" : "<<double(endClock-startClock)/CLOCKS_PER_SEC<<" s"<<endl;
    return;
}

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

template<typename T> //定义模板类型,使对各种数据类型都适用,如double,float,string
void SelectionSorting(T a[], int n){//选择排序算法
    for (int i = 0; i < n; i++){
        int minIndex = i;
        for (int j = i + 1; j < n; j++){
            if (a[j] < a[minIndex])
                minIndex = j;
        }
        swap(a[i], a[minIndex]);
    }
}

template<typename T>
void InsertionSortingImproved(T arr[], int n){
    for (int i = 0; i < n - 1; i++){
        T temp = arr[i+1];
        int j;
        for (j = i + 1; j > 0; j--){
            if (arr[j-1] > temp){
                arr[j] = arr[j-1];
            }
            else{
                break;
            }
        }
        arr[j] = temp;
    }
    return;
}

运行时间为:
在这里插入图片描述
可以看出归并排序比普通的插入排序确实快了很多倍
对于近乎有序的数组来说,运行下面的测试程序

int main()
{
    int n = 50000;
    //int *arr = generateRandomArray(n, 0, n);
    int *arr = generateNearlyOrderedArray(n, 10);//生成只有200个无序元素的数组
    int *arr2 = copyIntArray(arr, n);
    testSorting("InsertionSortingImproved", InsertionSortingImproved, arr, n);
    testSorting("MergeSorting", MergeSorting, arr2, n);
    delete[] arr;//最后删除数组开辟的空间
    delete[] arr2;
    return 0;
}

运行时间为
在这里插入图片描述
可以看出对于近乎有序的数组来说,插入排序的效果还是很好的,要优于归并排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值