逆序对模板(归并排序)

背景:给一列数a1,a2,a3,,,,,an,求它的逆序对数,即有多少个有序对(i,j),使得i < j但ai > aj,n高达10^6.

O(n*n)的枚举超时,所以用时间复杂度为O(nlogn)为归并排序进行统计逆序对数。

归并排序按照分治三步法:

划分问题:把序列元素个数分成尽量相等的两半

递归求解:把两半元素分别排序

合并问题:把两个有序表合并成一个

关于第3步,合并,根据算法入门经典上的讲解,加上自己的理解,用归并排序实现求逆序对的思想为:

递归求解已经将数组分为左半数组和右半数组,对于每一个右半数组的j,只求左半数组中大于j位置处的数的个数m-p,而m-p的总和就是逆序对的总数。详细理解如下:

在用临时空间存值过程中,只要左半数组中存在数<=右半数组j位置的数,那么必定会存入临时空间,所以当将右半数组j位置的数存入临时空间时,说明左半数组中的从p位置开始的数全大于右半数组j位置的数,这种情况下左半数组的个数和=m-p。

void merge_sort(int *a,int x,int y,int *b)
{
	if( y - x <= 1)//边界条件:只剩下两个元素 
		return;
	int m = x + (y-x)/2; 
	merge_sort(a,x,m,b);
	merge_sort(a,m,y,b);
	int p,q,i;
	p = i = x;
	q = m;
	while(p < m||q < y)
	{
		if( q >= y||(p < m&&a[p] <= a[q]))//从左半数组复制到临时空间 
			b[i++] = a[p++];
		else
		{
			b[i++] = a[q++];//从右半数组复制到临时空间 
			ans += m-p;//对于右边的q,统计左边比它大的元素个数m-p,则所有m-p之和就是答案 
		}
	} 
	for(int j = x; j < y; j ++)
		a[j] = b[j];
	return ;
}
int main()
{
	ans = 0;
	merge_sort(num,0,n,vis);//归并排序 
	printf("%d\n",ans);
	return 0;
}



你可能感兴趣的:(基础算法模板)