C++实现归并排序Mergesort(使用递归的方法)

归并排序的最坏运行时间为 O(NlogN) 。它的思想是把两个有序的子序列,通过一次遍历,合并且有序的放在第三个序列中。显然合并两个已排序的表的时间是线性的,最多进行 N1 次比较,其中N是元素的总个数。对总的运行时间进行推导如下:
假设N是2的幂次,从而每次总能分裂成两个相等的子列。对于 N=1 的子列,归并排序的时间为常数,我们记为1,那么我们可以得到如下的递推关系(参考Weiss的《数据结构与算法分析》第三版C++语言描述):

T(1)T(N)=1=2T(N/2)+N

其中,对于N个数的归并排序用的时间等于两个大小为 N/2 的归并排序所用的时间加上合并的时间。
把上面的式子两边同时除以N,得到以下的递推关系:
T(N)NT(N/2)N/2T(N/4)N/4T(2)2=2T(N/2)N/2+1=T(N/4)N/4+1=T(N/8)N/8+1=T(1)1+1

然后把上面的式子相加,消去等号两边相等的项,该过程称之为叠缩求和,最后得到的结果为
T(N)N=T(1)1+logN

因为总的方程个数等于执行归并排序中子列长度变化的次数 k=logN2
所以得到:
T(N)=NT(1)+NlogN

得证。

下面给出使用递归方法的程序:


#include<iostream>
#include<vector>
#include<random>
#include<ctime>
#include<iterator>
#include<algorithm>
using namespace std;

/*
 使用递归的归并排序
 该函数为驱动函数
*/
template<typename Comparable>
void mergeSortRecursive(vector<Comparable> &a)
{
    vector<Comparable> tmpArray(a.size());
    mergeSortRecursive(a, tmpArray, 0, a.size() - 1);
}

/*
 实现采用递归的归并排序
 a为原始数据
 tmpArray用于存放归并排序过程中的结果
 left表示迭代中子列的最左端的下标
 right表示迭代中子列的最右端的下标
*/
template<typename Comparable>
void mergeSortRecursive(vector<Comparable> &a,
    vector<Comparable> &tmpArray, int left, int right)
{
    if (left < right)
    {
        int center = (right + left) / 2;
        mergeSortRecursive(a, tmpArray, left, center);
        mergeSortRecursive(a, tmpArray, center + 1, right);
        merge(a, tmpArray, left, center + 1, right);
    }
}


/*
 归并排序中使用的合并函数
 a为原始数据
 tmpArray用于存放归并排序过程中的结果
 leftPos表示前一个子列的最左端的元素的下标
 rightPos表示后一个子列的最左端的元素的下标
 rightEnd表示后一个子列的最右端的元素的下标
*/
template<typename Comparable>
void merge(vector<Comparable> &a,
    vector<Comparable> &tmpArray, int leftPos, int rightPos, int rightEnd)
{
    int leftEnd = rightPos - 1;
    int tmpPos = leftPos; // 用来保存在合并过程中存放结果的临时向量的下标
    int numElements = rightEnd - leftPos + 1;

    //主循环,把数据合并
    while (leftPos <= leftEnd && rightPos <= rightEnd)
    {
        if (a[leftPos] < a[rightPos])
            tmpArray[tmpPos++] = a[leftPos++];
        else
            tmpArray[tmpPos++] = a[rightPos++];
    }

    //如果是因为后边子列的数据全部放在临时向量中导致主循环结束
    //则把前面没放完的数据依次放入临时变量中
    while (leftPos <= leftEnd)
        tmpArray[tmpPos++] = a[leftPos++];

    //同上处理前面子列数据全部先放入向量中的情况
    while (rightPos <= rightEnd)
        tmpArray[tmpPos++] = a[rightPos++];

    //注意!不能直接用a=tmpArray,因为可能只是复制子列
    for (int i = 0; i < numElements; ++i, --rightEnd)
        a[rightEnd] = tmpArray[rightEnd];

}



/*输出向量*/
template<typename T>
void printVector(vector<T> & v)
{
    copy(v.cbegin(), v.cend(), ostream_iterator<T>(cout, " "));
    cout << endl;
}

int main()
{
    vector<int> source;
    uniform_int_distribution<int> u(0, 1000);
    default_random_engine e(static_cast<unsigned int>(time(0)));
    for (int i = 0; i < 31; i++)
    {
        source.push_back(u(e));
    }

    cout << "排序前:" << endl;
    printVector(source);

    mergeSortRecursive(source);

    cout << "排序后:" << endl;
    printVector(source);

    return 0;
}

该程序对merge每次都调用相同的临时向量,因为每次调用merge的时候只有一个临时向量在使用,因此建立和待排向量等长的临时向量,每次使用的地方都是正在执行归并排序操作下标起始处。

执行结果如下:
这里写图片描述

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
归并排序是一种经典的排序算法,它的基本思想是将待排序的数组分成两个部分,将每个部分分别进行排序,最后将排好序的两个部分合并成一个有序的数组。下面是一份不使用递归实现归并排序C++ 代码: ```cpp #include <iostream> #include <vector> using namespace std; void merge(vector<int>& nums, int left, int mid, int right) { vector<int> tmp(right - left + 1); int i = left, j = mid + 1, k = 0; while (i <= mid && j <= right) { if (nums[i] <= nums[j]) { tmp[k++] = nums[i++]; } else { tmp[k++] = nums[j++]; } } while (i <= mid) { tmp[k++] = nums[i++]; } while (j <= right) { tmp[k++] = nums[j++]; } for (int p = 0; p < k; ++p) { nums[left + p] = tmp[p]; } } void mergeSort(vector<int>& nums) { int n = nums.size(); for (int step = 1; step < n; step *= 2) { for (int i = 0; i < n - step; i += step * 2) { int left = i; int mid = i + step - 1; int right = min(i + step * 2 - 1, n - 1); merge(nums, left, mid, right); } } } int main() { vector<int> nums = {3, 5, 1, 4, 2, 6}; mergeSort(nums); for (int num : nums) { cout << num << " "; } cout << endl; return 0; } ``` 该程序的主要思路是:按照步长(step)将数组分为若干个长度为step的子数组,然后将相邻的两个子数组合并成一个有序的大数组,直到整个数组排好序。需要注意的是,在合并两个子数组时,需要开辟一个临时数组来存放排好序的元素,最后再将排好序的元素复制回原数组。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值