逆序对及归并排序的整体思路求解过程分析

 逆序数

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。

Input

第1行:N,N为序列的长度(n <= 50000) 
第2 - N + 1行:序列中的元素(0 <= Aii <= 10^9)

Output

输出逆序数

Sample Input

4
2
4
3
1

Sample Output

4

逆序数的求法有两种,一种是树状数组,另一种就是利用分治法来求,我们这次只考虑用分治法来求逆序数。

很显然,对一个数组来说,我们选择这个这个数组的中点,假设这个数组为a,那么我们把它分隔成为了b,c两个数组

很显然,逆序对应该有以下三种情况:

1、存在于b数组的逆序对个数

2、存在于c数组的逆序对个数

3、存在于b,c组合在一起的逆序对个数

对于前两种,我们直接递归就可以得到答案,对于第三种,我们该怎么得到呢?

我们其实在整个递归的过程中是已经对b,c进行了排序的(没错,就是归并排序),也就是说在当前的状态下,b和c都是有序的

那么问题就简单了我们从b和c的第一项开始逐个进行比较,如果b[i]<=c[j],那么我们就把b放到原数组中,否则就把c[j]放到原数组中(我们是按照从小到大来排序的),在吧c[j]放到原数组的过程中,我们就开始统计逆序对的个数了:由于b和c是有序的,所以如果c[j]<=b[i],那么从b[i]以后的所有b中的元素都是大于c[j]的,因此逆序对的个数直接+=(b数组的大小-b[i]),就这样,我们得到了这次比较产生的逆序对的个数。同时也完成了归并排序,对于n个数,我们在整个过程中递归了log(n)次,每次合并的复杂度为O(n),因此整个的复杂度为O(n*log(n))。

#include<iostream>
#include<algorithm>
#include<vector>
#include<string.h>
using namespace std;
typedef long long ll;

vector<ll> a;
ll mergecount(vector<ll> &a)
{
	ll n=a.size();
	if(n<=1) return 0;
	ll cnt=0;
	
	vector<ll> b(a.begin(),a.begin()+n/2);
	vector<ll> c(a.begin()+n/2,a.end());
	
	cnt+=mergecount(b);//第一种逆序对
	cnt+=mergecount(c);//第二种逆序对
	
	ll ai=0,bi=0,ci=0;
	while(ai<n){//第三种逆序对的求解以及O(n)合并
		if(bi<b.size()&&(ci==c.size()||b[bi]<=c[ci])){
			a[ai++]=b[bi++];
		}
		else{
			cnt+=n/2-bi;
			a[ai++]=c[ci++];
		}
	}
	return cnt;
}

int main()
{
	ll n,x;
	while(cin>>n&&n){
		a.clear();
	for(ll i=0;i<n;i++){
		cin>>x;
		a.push_back(x);
	}
	cout<<mergecount(a)<<endl;//逆序对的个数
	for(int i=0;i<a.size();i++) cout<<a[i]<<" ";//排序后的数组
}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/duanghaha/article/details/81383998