【基本算法】归并排序

归并排序基础思想

归并排序主要运用了分治的思想
我们把归并排序分为两个部分:

  1. 分解:我们把无序的数列分成两个部分,对于每个部分,在分成更小的两个部分,直到数列只有一个元素为止
  2. 合并:我们把两个数列合并起来,成为一个排好序的一个数列

操作大体如下图
归并排序
分解只是把 C C C 数组分成 A , B A,B A,B 两个数组,不用考虑大小关系,所以比较容易
这里主要讲解一下和并的过程
合并是把 A , B A,B A,B 两个排好了的数组有序地合并起来
例如上图中最后一步

A = A= A= { 2 , 4 , 7 , 8 2, 4, 7, 8 2,4,7,8 }
B = B= B= { 1 , 3 , 5 , 6 1, 3, 5, 6 1,3,5,6 }
首先 1 < 2 1<2 1<2 ,所以首先插入 1 1 1
然后对比 2 2 2 3 3 3 2 < 3 2<3 2<3,插入 2 2 2
以此类推,最后 C = C= C= { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 1,2,3,4,5,6,7,8 1,2,3,4,5,6,7,8 }

所以我们合并时可以用两个变量保存 A A A B B B 数组从左开始判断到第几个数了( L L L L 2 L2 L2

如果 a [ L ] < a [ L 2 ] a[L] < a[L2] a[L]<a[L2] C C C 数组插入 a [ L ] a[L] a[L] L + + L++ L++
否则, C C C 数组插入 a [ L 2 ] a[L2] a[L2] L 2 + + L2++ L2++

归并排序求逆序数对

怎么在归并排序的同时求逆序数对呢?
先思考一个问题:在进行 { 5 , 6 5, 6 5,6 } 和 { 1 , 3 1 ,3 1,3 } 的排序时,关于 3 3 3 的有几个逆序数对(相对于之前, 3 3 3位置前进了几个)?
合并过程如下图
在这里插入图片描述
当插入 3 3 3 时, L = 1 , L 2 = 2 L=1,L2=2 L=1,L2=2 ,而 3 3 3 显然前进了两格,把 5 , 6 5,6 5,6 甩在后头,即关于此时关于 3 3 3 的逆序数对有两个
我们可以发现, L 2 L2 L2 所属位置数字的逆序数对大概为 每个序列总数(2) - L +1

代码

#include<bits/stdc++.h>
#define ll long long
const int N = 100005; 
ll a[N],b[N],cnt,n,k;
void Merge(ll l,ll mid,ll r){ //合并
	ll L=l,L2=mid+1,t=0;
	while(L<=mid && L2<=r)
	{
		if(a[L]>a[L2])
		{
			b[t++]=a[L2++]; //插入a[L2]
			cnt+=mid-L+1; //求逆序数对
		}
		else b[t++]=a[L++];
	} //有可能一个数列处理完了,但另一个没有处理完
	while(L<=mid) b[t++]=a[L++];
	while(L2<=r) b[t++]=a[L2++];
	for(int i=0;i<t;i++) 
		a[l+i]=b[i]; //把排好序的b数组复制回a数组
}
void Mergesort(ll l,ll r){
	if(l<r)
	{
		ll mid=(l+r)/2; //分成左右两部分处理
		Mergesort(l,mid);
		Mergesort(mid+1,r);
		Merge(l,mid,r); //合并
	}
}
int main(){
	scanf("%lld",&n);
	for(ll i=0;i<n;i++) scanf("%lld",&a[i]);
	Mergesort(0,n-1);
	for(ll i=0;i<n;i++) printf("%lld ",a[i]);
	printf("\n%lld",cnt);
}
  • 62
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值