求解逆序对的个数(由归并排序衍生出的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;
}
发布了235 篇原创文章 · 获赞 51 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/ASJBFJSB/article/details/103138972