归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(N*logN)。
下面为方法四的代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int a[maxn], t[maxn];
long long cnt;
//oid prt(int n) {
// for(int i = 1; i <= n; i++) printf("%d ", a[i]);
// printf("\n");
//}
void solve(int l, int r) {
if(l >= r) return;
int mid = (l + r) >> 1;
solve(l, mid);//分治左边
solve(mid + 1, r);//分治右边 // printf("solve(%d, %d)\n", l, r); // prt(5);// printf("cnt=%d\n", cnt);
//分治过程中已经排好序,用两个指针扫描一遍两边
for(int i = mid, j = r; i >= l; i--) {
while(a[i] <= a[j] && j > mid) j--;
if(j - mid == 0) break;
cnt += j - mid;//这一步是在统计跨两侧的逆序对个数,这步的操作基础是一左一右两个已经排好序的子序
//列
}// printf("cnt=%d\n", cnt);
//排序,类似归并排序,两个指针扫描两边
for(int i = l; i <= mid; i++) t[i] = a[i];//复制
for(int i = l, j = mid + 1; ; ) {//t代表左侧序列,a中存右侧序列和合并后序列,这一步是排序合
//并
if(t[i] > a[j]) a[i+j-mid-1] = a[j++];//把两个指针较小的一个放到开始
else a[i+j-mid-1] = t[i++];
if(i > mid) break;//左侧的已经并入a,不用再进行其他操作,因为右侧数字本来就保存在a中
if(j > r) {//右侧的已经安排完毕,此时需要把t中左侧的数字全部安排进入a中
while(i <= mid) a[i+j-mid-1] = t[i++];
break;
}
}
}
int main() {
int T; scanf("%d", &T);
while(T--) {
int n; scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
cnt = 0;
solve(1, n);
printf("%lld\n", cnt);
}
return 0;
}