逆序:在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。
一、暴力做法
int count(int *a, int n) {
int res = 0;
for (int i=0; i<n-1; ++i) {
for (int j=1; j<n; ++j) {
if (a[i] > a[j]) res ++;
}
}
return res;
}
可以看到,上边算法的时间复杂度为O(n^2),有没有效率更高的算法呢,其实在归并排序中,当进行两个有序数组合并时就会两两元素的比较。此时会出现,当第一个数组的某元素a[i]大于第二数组中的某元素a[j]时,则一个数组处于[i, m]区间的所有元素都会大于第二个数组的当前元素a[j]。这样做的好处是不需要将数组中的元素依次进行两两比较,一次比较就能处理一个大区间,因此算法的效率得到了提升。
二、归并排序
#include <iostream>
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll a[N], t[N];
ll res = 0;
void mergeSort(ll *a, int l, int r) {
if (l>=r) return;
int m = (r-l)/2 + l;
mergeSort(a, l, m);
mergeSort(a, m+1, r);
//i为第一个区间的起始下标 第一个区间[l, m]
//j为第二个区间的起始下标 第二个区间[m+1, r]
//k为临时数组的起始下标
int i = l, r = m+1, k = 0;
while (i <= m && j <= r) {
if (a[i] <= a[j]) t[k ++] = a[i ++];
else {
//a[i] > a[j] && i < j 构成了逆序对 而此时 a[i]到a[m]的所有元素均大于a[j]
//a[i,....,m]的长度为 m-i+1
res += m-i+1;
t[k ++] = a[j ++];
}
}
while (i <= m) t[k ++] = a[i ++];
while (j <= r) t[k ++] = a[j ++];
for (int i=l, k=0; i<=r; ++i, ++k) a[i] = t[k];
}
int main() {
ios::sync_with_stdio(false);
int n;
cin >> n;
for (int i=0; i<n; ++i) cin >> a[i];
mergeSort(a, 0, n-1);
cout << res << endl;
return 0;
}