归并排序
1.归并排序是利用的分治的策略进行的排序,用的二分,每次都分成两部分,这两部分分别有序,在合并为一个整体有序。
2.归并排序是稳定排序
归并排序分的部分采用递归的形式:
void mysort(int L,int R){
if(L>=R) return ;
int mid=(L+R)>>1;
mysort(L,mid);//下面两步就是二分
mysort(mid+1,R);
bin(L,R,mid);// 归并操作
}
代码看上去并不复杂,关键在于合并部分:
void bin(int L,int R,int mid){
int i=L,j=mid+1;
int k=L;
while(i<=mid&&j<=R){//相当于两个指针,分别指向两边的头部(两边皆为有序的),所以当那一边更小,就把他加入到b数组中然后该指针++
if(a[i]<a[j]){
b[k++]=a[i++];
}
else{
ans+=mid-i+1;//求逆序对
b[k++]=a[j++];
}
}
while(i<=mid) b[k++]=a[i++];
while(j<=R) b[k++]=a[j++];
for(int t=L;t<=R;t++) a[t]=b[t];
}
归并排序的一个小运用就是求逆序对 给一道例题:
链接:https://ac.nowcoder.com/acm/problem/15163
来源:牛客网
题目描述
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。比如一个序列为4 5 1 3 2, 那么这个序列的逆序数为7,逆序对分别为(4, 1), (4, 3), (4, 2), (5, 1), (5, 3), (5, 2),(3, 2)。
输入描述:
第一行有一个整数n(1 <= n <= 100000), 然后第二行跟着n个整数,对于第i个数a[i],(0 <= a[i] <= 100000)。
输出描述:
输出这个序列中的逆序数
示例1
输入
5
4 5 1 3 2
输出
7
求逆序对:你观察归并排序的归并部分
while(i<=mid&&j<=R){//相当于两个指针,分别指向两边的头部(两边皆为有序的),所以当那一边更小,就把他加入到b数组中然后该指针++
if(a[i]<a[j]){
b[k++]=a[i++];
}
else{
b[k++]=a[j++];
}
}
当右边的部分当前位置更小时,那现在是不是右边部分当前指针指的位置的右边到mid都比右边当前的大。所以
逆序对的值就只需在这时加上一个mid-i+1就完了
完整代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+5;
int a[N],b[N];//b为归并排序的辅助数组
ll ans=0;
void bin(int L,int R,int mid){
int i=L,j=mid+1;
int k=L;
while(i<=mid&&j<=R){//相当于两个指针,分别指向两边的头部(两边皆为有序的),所以当那一边更小,就把他加入到b数组中然后该指针++
if(a[i]<a[j]){
b[k++]=a[i++];
}
else{
ans+=mid-i+1;//求逆序对
b[k++]=a[j++];
}
}
while(i<=mid) b[k++]=a[i++];
while(j<=R) b[k++]=a[j++];
for(int t=L;t<=R;t++) a[t]=b[t];
}
void mysort(int L,int R){
if(L>=R) return ;
int mid=(L+R)>>1;
mysort(L,mid);//下面两步就是二分
mysort(mid+1,R);
bin(L,R,mid);// 归并操作
}
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
mysort(0,n-1);
cout<<ans<<endl;
return 0;
}