分治经典——归并排序(MergeSort)

分治法

分治,字面上的解释是"分而治之",就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。在计算机科学中,分治法就是运用分治思想的一种很重要的算法。分治法是很多高效算法的基础,如排序算法(快速排序,归并排序)傅立叶变换(快速傅立叶变换) 等等。

归并排序示例代码

void mergex(int l,int mid,int r){
	int z=l,y=mid+1;
	p=l;
	while(z<=mid && y<=r){
		if(data[z]<=data[y]){
			tmp[p++]=data[z++]; 
		}else{
			tmp[p++]=data[y++];
		}
	}
	while(z<=mid){tmp[p++]=data[z++];}
	while(y<=r){tmp[p++]=data[y++];}
	for(int i=l;i<=r;i++)
		data[i]=tmp[i];
	return;
}

void merge(int l,int r){
	if(l<r){
		int mid=(l+r)>>1;
		merge(l,mid);  //第一个
		merge(mid+1,r);  //第二个
		mergex(l,mid,r);
	}
	return;
}

这里为了帮助大家理解,我写成两个部分。
形象的说:
merge 部分是mergex 部分是

归并排序样例模拟

这里为了表达方便,规定上面的merge为第一个,下面的为第二个(注释里也有标注)
假设我们排序下列数据:
2 1 3 5 4 6 9
那么就进入函数merge(1,7)
在这里插入图片描述
那么有:
在这里插入图片描述
于是再次进入第一个merge(1,4)
所以有:
在这里插入图片描述
还是可以继续,进入第一个merge(1,2)
在这里插入图片描述
接下来进入merge(1,1)
但是规定l<r 所以刚进入merge(1,1) 就立马return 了。
然后进入merge(2,2) 同理,也是return

接下来进入mergex(1,1,2)
进入mergex函数后就会多很多变量,就像酱紫
在这里插入图片描述
先看这一条:

while(z<=mid && y<=r){
	if(data[z]<=data[y]){
		tmp[p++]=data[z++]; 
	}else{
		tmp[p++]=data[y++];
	}
}

发现第一个判断data[z]<=data[y] 无法通过,于是执行else 的tmp[p++]=data[y++];
所以:
在这里插入图片描述
接着发现while循环条件没有满足了,于是出了循环。
但是我们发现左边边那一块还都没填在tmp数组里面。
所以我们使用两个while循环分别处理剩余没填入的东西。
由于我们已经满足把小的填到左,大的填到右的原则,而且左边或者右边都已经有序,因此直接填入。

while(z<=mid){tmp[p++]=data[z++];}
while(y<=r){tmp[p++]=data[y++];}
for(int i=l;i<=r;i++)
	data[i]=tmp[i];

最后,我们把临时存在tmp里的数据填入原来的data数组。
所以有:
在这里插入图片描述

接下来从第一个第一个merge(1,2) 出来,进入第二个merge(3,4)
在这里插入图片描述
然后由第二个merge(3,4) 进入第一个merge(3,3)
但是规定l<r 所以刚进入merge(3,3) 就立马return 了。
同理,进入merge(3,3) 也是一样的。
这里不多做解释。

然后接下来就是mergex(1,2,4)
就像酱紫:
在这里插入图片描述
首先判断data[z]<=data[y] ,然后发现正确(data[1]<=data[3] )。
于是执行tmp[p++]=data[z++];
结果就是酱紫:
在这里插入图片描述
然后再比较一轮,此时data[z]<=data[y] 仍然成立(data[2]<=data[3]
所以有:
在这里插入图片描述
接着发现while循环条件没有满足了,于是出了循环。

接下来笔者无需继续模拟了,请拿起你的笔自己模拟一下,如果上述过程没问题的话,我相信最终你也可以完成排序。

代码示例

下面提供完整代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define maxm 1000001
long long data[maxm],tmp[maxm],ans,n,p;

void mergex(int l,int mid,int r){
	int z=l,y=mid+1;
	p=l;
	while(z<=mid && y<=r){
		if(data[z]<=data[y]){
			tmp[p++]=data[z++]; 
		}else{
			tmp[p++]=data[y++];
		}
	}
	while(z<=mid){tmp[p++]=data[z++];}
	while(y<=r){tmp[p++]=data[y++];}
	for(int i=l;i<=r;i++)
		data[i]=tmp[i];
	return;
}

void merge(int l,int r){
	if(l<r){
		int mid=(l+r)>>1;
		merge(l,mid);
		merge(mid+1,r);
		mergex(l,mid,r);
	}
	return;
}

int main(){
	freopen("min.txt","r",stdin);
	std::ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>data[i];
	merge(1,n);
	for(int i=1;i<=n;i++){cout<<tmp[i]<<" ";}
	return 0;
}

实际上,为了让代码看起来更简单,熟练的同学可以把上下两个函数mergemergex 合并。
就像我下面提供的写法,推荐使用这种:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxm=100005; 
int n,d[maxm],tmp[maxm];

void mergesort(int l,int r){
	if(l>=r) return;
	int mid=(l+r)>>1;
	mergesort(l,mid);
	mergesort(mid+1,r);
	
	int z=l,y=mid+1,p=l;
	while(z<=mid&&y<=r){
		if(d[z]<=d[y]) tmp[p++]=d[z++];
		else tmp[p++]=d[y++];
	}

	while(z<=mid) tmp[p++]=d[z++];
	while(y<=r) tmp[p++]=d[y++];
	for(int i=l;i<=r;i++)
		d[i]=tmp[i];
	return;
}

int main(){
	std::ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>d[i];
	
	mergesort(1,n);
	
	for(int i=1;i<=n;i++)
		cout<<d[i]<<" ";
	return 0;
}
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值