逆序:在一個排列中,如果一對數的前後位置與大小順序相反,即前面的數大於後面的數,那麼它們就稱爲一個逆序。
一、暴力做法
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;
}