归并排序基础思想
归并排序
主要运用了分治
的思想
我们把归并排序
分为两个部分:
- 分解:我们把
无序
的数列分成两个部分,对于每个部分,在分成更小的两个部分,直到数列只有一个元素
为止- 合并:我们把两个数列合并起来,成为一个
排好序
的一个数列
操作大体如下图
分解
只是把
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);
}