归并排序算法 C++实现与时间复杂度(考过)恋上数据结构笔记

复习梗概

  1. 画图,自己整个数组,看代码写步骤,这个对理解归并排序还是很有必要的
  2. 合并两个有序数组的merge函数写法
  3. 时间复杂度的分析方法!!!
  4. 其实我觉得去b站找个动态的步骤分解视频也是不错的复习方法,当然要自己先回想

归并排序特点

  1. 稳定,快速
  2. 递归调用,分治思想
  3. 合并两个有序数组的思想
  4. 时间复杂度O(nlogn),这个时间复杂度的分析方法很重要一定要掌握

归并排序时间复杂度分析(某校知能情报考过)

恋上数据结构笔记 归并排序与时间复杂度
这个图可能会很有用
在这里插入图片描述
图片来自:恋上数据结构结构与算法第二季-李明杰老师


归并排序本体函数

从输出也能大致看出来归并排序的归并顺序,每次merge都会打印一次数组
在这里插入图片描述
图片来自:恋上数据结构结构与算法第二季-李明杰老师

void mergeSort(vector<int> &array, int begin, int end)
{
    //有点类似树的递归访问,每次调用都把当前数组一分为二,直到不可再分就return,两个都return后,merge成一个有序数组,再不断往回merge成有序数组
    if (end - begin + 1 < 2)
    {
        return;
    }
    int mid = (begin + end) / 2;
    mergeSort(array, begin, mid);
    mergeSort(array, mid+1, end);
    merge(array, begin, end);
    vectorPrint(array);
}
输入数组:
6 9 3 1 2 0 8 29 15 11 10
归并排序基础版
6 9 3 1 2 0 8 29 15 11 10
3 6 9 1 2 0 8 29 15 11 10
3 6 9 1 2 0 8 29 15 11 10
3 6 9 0 1 2 8 29 15 11 10
0 1 2 3 6 9 8 29 15 11 10
0 1 2 3 6 9 8 29 15 11 10
0 1 2 3 6 9 8 15 29 11 10
0 1 2 3 6 9 8 15 29 10 11
0 1 2 3 6 9 8 10 11 15 29
0 1 2 3 6 8 9 10 11 15 29
算法用时:(微秒)
[AlgoTime: 20007 us]
输入数组:
6 9 3 1 2 0 8 29 15 11 10
归并排序归并换个写法
6 9 3 1 2 0 8 29 15 11 10
3 6 9 1 2 0 8 29 15 11 10
3 6 9 1 2 0 8 29 15 11 10
3 6 9 0 1 2 8 29 15 11 10
0 1 2 3 6 9 8 29 15 11 10
0 1 2 3 6 9 8 29 15 11 10 
0 1 2 3 6 9 8 15 29 11 10
0 1 2 3 6 9 8 15 29 10 11
0 1 2 3 6 9 8 10 11 15 29
0 1 2 3 6 8 9 10 11 15 29
算法用时:(微秒)
[AlgoTime: 22006 us]

merge函数!!重要(两个有序数组合并成一个有序数组)

图片来自:恋上数据结构结构与算法第二季-李明杰老师

  1. 这个函数比较重要
  2. 重点理解三个指针的操作和循环终止的条件
  3. 这个写法不便于理解,后面那个比较好理解
//merge函数就是将两个有序数组合并成一个有序数组,由于归并排序中,这两个数组都在一个数组内,所以这里参数begin是左数组的第一位,end是右数组的最后一位
//这里的begin,mid,end都代表的是数组下标
//merge的整体思想是,两个有序数组,两个指针分别指向首位,指针指向的二者进行比较,小的先放入新数组,同时插入的元素对应数组指针++,另一个不变,接着比
//还是画图比较好理解,但这个也不难
void merge(vector<int> &array, int begin, int end)
{
    vector<int> temp = {};//定义临时数组,用于暂时存放合并并排序的有序数组,最后再覆盖给array
    int mid = (begin + end) / 2;
    //求mid,mid是左数组的终端,mid+1是右数组的起点,这和mergeSort函数中上面两个递归调用的begin与mid和end是一样的
    int tempPointer = 0;
    int leftBeginPointer = begin;//不要想当然以为左数组起点下标一定是0,这要看你传进来的是从哪开始的数组
    int rightBeginPointer = mid + 1;
    //这里定义了三个指针,分别指向临时数组的下一个插入位置(vector就不需要了,就是个摆设),左数组的下一个插入元素,右数组的下一个插入元素
    while (tempPointer != end + 1)//当临时数组长度已经等于右数组末尾长度(左右数组长度和),说明都插入了,结束循环
    {
        if (array[leftBeginPointer] <= array[rightBeginPointer])
        {
            temp.push_back(array[leftBeginPointer]);
            tempPointer++;
            leftBeginPointer++;
        }
        else if (array[rightBeginPointer] < array[leftBeginPointer])
        {
            temp.push_back(array[rightBeginPointer]);
            tempPointer++;
            rightBeginPointer++;
        }
        //若检测一个数组已经全部插入,则直接把另一个数组剩余元素全部插入退出即可
        if (leftBeginPointer == mid + 1)
        {
            while (rightBeginPointer != end + 1)
            {
                temp.push_back(array[rightBeginPointer]);
                tempPointer++;
                rightBeginPointer++;
            }
            break;
        }
        else if (rightBeginPointer == end + 1)
        {
            while (leftBeginPointer != mid + 1)
            {
                temp.push_back(array[leftBeginPointer]);
                tempPointer++;
                leftBeginPointer++;
            }
            break;
        }
    }
    //用临时数组覆盖array对应位置的数组
    for (int i = 0; i < end - begin + 1; i++)
    {
        array[begin + i] = temp[i];
    }
}

merge函数写法2

这是merge函数的另一种写法,因为写法不是唯一的,多看看以免以后考到不会
这个是先有一个数组全部插入了就截止,然后再把另一个数组剩余的全部插入

void merge2nd(vector<int> &array, int begin, int end)
{
    vector<int> temp = {};//定义临时数组,用于暂时存放合并并排序的有序数组,最后再覆盖给array
    int mid = (begin + end) / 2;
    //求mid,mid是左数组的终端,mid+1是右数组的起点,这和mergeSort函数中上面两个递归调用的begin与mid和end是一样的
    int tempPointer = 0;
    int leftBeginPointer = begin;//不要想当然以为左数组起点下标一定是0,这要看你传进来的是从哪开始的数组
    int rightBeginPointer = mid + 1;
    //这里定义了三个指针,分别指向临时数组的下一个插入位置,左数组的下一个插入元素,右数组的下一个插入元素
    
    while(leftBeginPointer<mid+1&&rightBeginPointer<end+1){
        if (array[leftBeginPointer] <= array[rightBeginPointer])
        {
            temp.push_back(array[leftBeginPointer]);
            tempPointer++;
            leftBeginPointer++;
        }
        else if (array[rightBeginPointer] < array[leftBeginPointer])
        {
            temp.push_back(array[rightBeginPointer]);
            tempPointer++;
            rightBeginPointer++;
        }
    }
    while(leftBeginPointer<mid+1){
        temp.push_back(array[leftBeginPointer]);
        tempPointer++;
        leftBeginPointer++;
    }
    while(rightBeginPointer<end+1){
        temp.push_back(array[rightBeginPointer]);
        tempPointer++;
        rightBeginPointer++;
    }
    //用临时数组覆盖array对应位置的数组
    for (int i = 0; i < end - begin + 1; i++)
    {
        array[begin + i] = temp[i];
    }
}



完整代码

#include <iostream>
#include <vector>
#include "MeasureAlgoTime.hpp"
using namespace std;

//
void vectorPrint(vector<int> &array)
{
    for (int i = 0; i < array.size(); i++)
    {
        cout << array[i] << ' ';
    }
    cout << endl;
}

//merge函数就是将两个有序数组合并成一个有序数组,由于归并排序中,这两个数组都在一个数组内,所以这里参数begin是左数组的第一位,end是右数组的最后一位
//这里的begin,mid,end都代表的是数组下标
//merge的整体思想是,两个有序数组,两个指针分别指向首位,指针指向的二者进行比较,小的先放入新数组,同时插入的元素对应数组指针++,另一个不变,接着比
//还是画图比较好理解,但这个也不难
void merge(vector<int> &array, int begin, int end)
{
    vector<int> temp = {};//定义临时数组,用于暂时存放合并并排序的有序数组,最后再覆盖给array
    int mid = (begin + end) / 2;
    //求mid,mid是左数组的终端,mid+1是右数组的起点,这和mergeSort函数中上面两个递归调用的begin与mid和end是一样的
    int tempPointer = 0;
    int leftBeginPointer = begin;//不要想当然以为左数组起点下标一定是0,这要看你传进来的是从哪开始的数组
    int rightBeginPointer = mid + 1;
    //这里定义了三个指针,分别指向临时数组的下一个插入位置(vector就不需要了,就是个摆设),左数组的下一个插入元素,右数组的下一个插入元素
    while (tempPointer != end + 1)//当临时数组长度已经等于右数组末尾长度(左右数组长度和),说明都插入了,结束循环
    {
        if (array[leftBeginPointer] <= array[rightBeginPointer])
        {
            temp.push_back(array[leftBeginPointer]);
            tempPointer++;
            leftBeginPointer++;
        }
        else if (array[rightBeginPointer] < array[leftBeginPointer])
        {
            temp.push_back(array[rightBeginPointer]);
            tempPointer++;
            rightBeginPointer++;
        }
        //若检测一个数组已经全部插入,则直接把另一个数组剩余元素全部插入退出即可
        if (leftBeginPointer == mid + 1)
        {
            while (rightBeginPointer != end + 1)
            {
                temp.push_back(array[rightBeginPointer]);
                tempPointer++;
                rightBeginPointer++;
            }
            break;
        }
        else if (rightBeginPointer == end + 1)
        {
            while (leftBeginPointer != mid + 1)
            {
                temp.push_back(array[leftBeginPointer]);
                tempPointer++;
                leftBeginPointer++;
            }
            break;
        }
    }
    //用临时数组覆盖array对应位置的数组
    for (int i = 0; i < end - begin + 1; i++)
    {
        array[begin + i] = temp[i];
    }
}

void merge2nd(vector<int> &array, int begin, int end)
{
    vector<int> temp = {};//定义临时数组,用于暂时存放合并并排序的有序数组,最后再覆盖给array
    int mid = (begin + end) / 2;
    //求mid,mid是左数组的终端,mid+1是右数组的起点,这和mergeSort函数中上面两个递归调用的begin与mid和end是一样的
    int tempPointer = 0;
    int leftBeginPointer = begin;//不要想当然以为左数组起点下标一定是0,这要看你传进来的是从哪开始的数组
    int rightBeginPointer = mid + 1;
    //这里定义了三个指针,分别指向临时数组的下一个插入位置,左数组的下一个插入元素,右数组的下一个插入元素
    
    while(leftBeginPointer<mid+1&&rightBeginPointer<end+1){
        if (array[leftBeginPointer] <= array[rightBeginPointer])
        {
            temp.push_back(array[leftBeginPointer]);
            tempPointer++;
            leftBeginPointer++;
        }
        else if (array[rightBeginPointer] < array[leftBeginPointer])
        {
            temp.push_back(array[rightBeginPointer]);
            tempPointer++;
            rightBeginPointer++;
        }
    }
    while(leftBeginPointer<mid+1){
        temp.push_back(array[leftBeginPointer]);
        tempPointer++;
        leftBeginPointer++;
    }
    while(rightBeginPointer<end+1){
        temp.push_back(array[rightBeginPointer]);
        tempPointer++;
        rightBeginPointer++;
    }
    //用临时数组覆盖array对应位置的数组
    for (int i = 0; i < end - begin + 1; i++)
    {
        array[begin + i] = temp[i];
    }
}

void mergeSort(vector<int> &array, int begin, int end)
{
    //有点类似树的递归访问,每次调用都把当前数组一分为二,直到不可再分就return,然后merge成一个有序数组,再不断merge
    if (end - begin + 1 < 2)
    {
        return;
    }
    int mid = (begin + end) / 2;
    mergeSort(array, begin, mid);
    mergeSort(array, mid+1, end);
    merge(array, begin, end);
    vectorPrint(array);
}

void mergeSort2nd(vector<int> &array, int begin, int end)
{
    if (end - begin + 1 < 2)
    {
        return;
    }
    int mid = (begin + end) / 2;
    mergeSort2nd(array, begin, mid);
    mergeSort2nd(array, mid+1, end);
    merge2nd(array, begin, end);
    vectorPrint(array);
}

int main()
{
    Tools::Time::AlgoTimeUs time1;
    Tools::Time::AlgoTimeUs time2;
    Tools::Time::AlgoTimeUs time3;

    vector<int> array;
    array = {6, 9, 3, 1, 2, 0, 8, 29, 15, 11, 10};
    vector<int> array2 = array;
    vector<int> array3 = array;

    cout << "输入数组:" << endl;
    vectorPrint(array);
    time1.start();
    cout << "归并排序基础版" << endl;
    mergeSort(array, 0, array.size() - 1);
    cout << "算法用时:(微秒)";
    time1.printElapsed();

    cout << "输入数组:" << endl;
    vectorPrint(array2);
    time1.start();
    cout << "归并排序归并换个写法" << endl;
    mergeSort(array2, 0, array2.size() - 1);
    cout << "算法用时:(微秒)";
    time1.printElapsed();
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值