归并排序的两种写法性能分析

写在前面:这篇博客来源于对一串归并排序代码的修改,本文将指出其错误并且将修改后代码的性能与另一种写法的性能进行对比。

首先来看下面一串代码 从代码语义上来理解并没有什么问题 但在程序运行过程中实际上会出现一定错误

#include <iostream>
using namespace std;
#define N 7
typedef int Rcd;
int Temp[N];
void Merge (Rcd R[], int low, int mid, int high, Rcd T[]){
  int P_left=low, P_right=mid+1, P_result=low;
  while(P_left<=mid && P_right<=high ){
     if(R[P_left]<R[P_right]){
         T[P_result]=R[P_left]; P_result++;P_left++;
     }
     else{
         T[P_result]=R[P_right];P_result++; P_right++;
     }
  }
   while(P_left<=mid){
        T[P_result]=R[P_left];   P_result++;  P_left++;
   }
   while(P_right<=high ) {
       T[P_result]=R[P_right];  P_result++;  P_right++;
   }
}

void MSort( Rcd R[], int low, int high, Rcd T[]){
  //对R[low..high]进行归并排序,结果存入T[low..high]
    if( low == high ) T[low]=R[low]; //递归边界
    else{
        int mid=(low+high)/2;
        MSort(R, low , mid,Temp); //左侧递归
        MSort(R, mid+1, high,Temp);//右侧递归
        Merge(Temp, low, mid, high, T); //归并
    }
}

int main(){
    int R[7]={1,3,5,2,7,4,9};
    MSort(R,0,6,R);
    for(int i=0;i<7;i++)
        cout<< R[i]<<"  ";
    return 0;
}

最终输出结果为 1 2 2 2 4 4 9
那么存在的问题是什么呢? 实际上是数组在更新时自身发生了冲突
通过分析我们发现:在程序运行的大部分时间里都是Temp数组在更新自己 这也就导致了Temp自身会发生冲突
这里给出一个例子:当对数组中的52进行归并时,P_left=2,P_right=3,P_result=2 放在代码中考虑,即R[2]=5, R[3]=2
5 > 2 故使得T[2]=2 即原来的52覆盖掉了 影响了后面本应当使得T[3]=5的操作
回到刚才所说的 RT实质上都是Temp 最终导致了错误
因此为了避免同一数组发生冲突应当开一个辅助数组来拷贝当前数组的值
只需修改Merge函数部分 如下:

void Merge (Rcd R[], int low, int mid, int high, Rcd T[]){
  int P_left=low, P_right=mid+1, P_result=low;
  int r[N];
  memcpy(r, R, sizeof r);
  while(P_left<=mid && P_right<=high ){
     if(r[P_left]<r[P_right]){
         T[P_result]=r[P_left]; P_result++;P_left++;
     }
     else{
         T[P_result]=r[P_right];P_result++; P_right++;
     }
  }
   while(P_left<=mid){
        T[P_result]=r[P_left];   P_result++;  P_left++;
   }
   while(P_right<=high ){
       T[P_result]=r[P_right];  P_result++;  P_right++;
   }
}
问题解决完了 下面我们来分析这种写法与另一种写法的性能

先贴一个另一种写法的代码 记为写法2 同时上述代码记为写法1

#include <iostream>
using namespace std;
#define N 100010
typedef int Rcd;
int T[N];
void Merge (Rcd R[], int low, int mid, int high){       
	int P_left=low, P_right=mid+1, P_result=low;
  while(P_left<=mid && P_right<=high ){
     if(R[P_left]<=R[P_right]){
         T[P_result]=R[P_left]; P_result++;P_left++; 
     }
     else{
         T[P_result]=R[P_right];P_result++; P_right++; 
     }
  }
   while(P_left<=mid){
        T[P_result]=R[P_left];   P_result++;  P_left++; 
   } 
   while(P_right<=high ) {
       T[P_result]=R[P_right];  P_result++;  P_right++; 
   }
   for(int i=low;i<=high;i++) R[i]=T[i]; //从T写回R,注意对比教材
} 


void MSort( Rcd R[], int low, int high){
  //对R[low..high]进行归并排序
    if( low == high ) return; //递归边界       
    else{
        int mid=(low+high)/2;
        MSort(R, low , mid); //左侧递归
        MSort(R, mid+1, high);//右侧递归
        Merge(R, low, mid, high); //归并
    }
}


int main(){
    int R[N];
    int n;
    cin >> n;
    for(int i = 0; i < n; i ++ )
        cin >> R[i];
    MSort(R,0,n - 1);
    for(int i=0;i<n;i++)
        cout<< R[i]<<" ";
    return 0;
}

两者的时间复杂度整体上来看都是 O ( n l o g n ) O(nlogn) O(nlogn)的 但是:
写法1的除了函数传参要多辅助数组①以外 还要针对辅助数组再建一个辅助数组②(也就是上面代码中的r数组)这就使得建立的辅助数组②每次都要拷贝一下辅助数组① 即每次进行Merge操作都有 O ( n ) O(n) O(n)的时间复杂度
而写法2每次执行for(int i=low;i<=high;i++) R[i]=T[i];次数一定是小于等于n的(high-low+1次) 显然相较于写法1更快一些

在空间复杂度上:就像上面提到的 写法1需要针对Temp数组额外开一个辅助数组r 而写法2就不需要 故写法1的空间复杂度上要大一些

在洛谷的快速排序模板题中提交验证也是这样的 题目链接
下面的提交记录为写法1 上面为写法2

综上所述:我认为写法2不管在时间复杂度还是空间复杂度上都要优于写法1 同时写法2也更好理解

尾语:

感谢您观看我的文章 如有错误欢迎在评论区指正~

  • 26
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值