逆序对目前我所知的有三种解法
首先是最简单的冒泡排序,当每次前面一个数比后面一个数大时就会交换,因此可以用冒泡排序来求逆序对
代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,sum=0;
cin>>n;
int a[10001];
for(int i=1;i<=n;i++) scanf("%d
",&a[i]);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(a[i]>a[j]) sum++;
printf("%d",sum);
return 0;
}
两个(for)循环,复杂度为(O(n^2))
接下来是归并排序,由于每次(merge)的时候会判断数的大小,所以归并排序可以用来求逆序对
代码:
#include<cstdio>
#define ll long long
using namespace std;
const int maxn=5e5+5;
int a[maxn],r[maxn],n;
ll ans=0;
void msort(int s,int t){
if(s==t) return ;
int mid=s+t>>1;
msort(s,mid),msort(mid+1,t);
int i=s,j=mid+1,k=s;
while(i<=mid&&j<=t)
if(a[i]<=a[j]) r[k++]=a[i++];
else r[k++]=a[j++],ans+=(ll)mid-i+1;
while(i<=mid) r[k]=a[i],k++,i++;
while(j<=t) r[k]=a[j],k++,j++;
for(int i=s;i<=t;i++) a[i]=r[i];
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
msort(1,n);
printf("%lld
",ans);
return 0;
}
复杂度为(O(nlogn))
最后是树状数组的解法
首先需要把数组离散化,然后从小到大排序,加入树状数组里维护
由于排完序是从小到大丢到树状数组里面的
所以现在加入的数字一定比之后加入的数字大
查询这个数前面的数就可以求得逆序对的个数了
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=500005;
inline ll read(){
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
ll n,a[N],b[N],f[N],ans;
inline bool cmp(const ll &i,const ll &j){return b[i]>=b[j];}
inline ll lowbit(ll x){return x&-x;}
inline void add(ll x){while(x<=n) ++f[x],x+=lowbit(x);return;}
inline void query(ll x){while(x) ans+=f[x],x-=lowbit(x);return;}
int main(){
n=read();
for(ll i=1;i<=n;++i) b[i]=read(),a[i]=i;
stable_sort(a+1,a+1+n,cmp);
for(ll i=1;i<=n;++i) add(a[i]),query(a[i]-1);
printf("%lld",ans);
return 0;
}
复杂度为(O(nlogn))
推荐使用树状数组,毕竟树状数组能做的东西比归并排序多,但是两种方式都是必须要掌握的
一些练习题:
如果能把这些题目都写完了,那么您对逆序对一定掌握的非常熟练了