求解逆序對的個數(由歸併排序衍生出的O(nlogn)時間複雜度的算法)

逆序:在一個排列中,如果一對數的前後位置與大小順序相反,即前面的數大於後面的數,那麼它們就稱爲一個逆序。

一、暴力做法

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章