求数组的逆序数

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

现在,给你一个N个元素的序列,请你判断出它的逆序数是多少。

比如 1 3 2 的逆序数就是1。

方法1::O(n^2)这个就不说了。

方法2: 借助归并排序达到O(nlgn)的时间复杂度,当然牺牲了一点空间。

这里主要描述一下方法2,前提是你需要懂一点归并排序的原理。举个例子,我们对{ 4 3 5 2 }做归并排序(升序)。一步一步的归并过程分解如下图所示。

a                                4352

b                 43                            52

c        4                 3             5             2                 

d                 34                            25

e                                2345

如上图所示,递归到第c行时,递归结束,开始回溯(归并)。在归并排序过程中,归并是在Merge函数中,对左半边数组和右半边数组做归并。先看第c行最左边的4和3,在归并的时候会判断这两个数的大小,4>3,所以这趟归并完结果为34。显然,我们在归并这里做比较的时候可以记录下来前半边数组比后半边数组大的对数。再看第d行,这里左半边数组是34,右半边是25,都已经有序了。按照归并排序,我们先要比较3和2的大小,然后比较3和5,再比较4和5。只有3是大于2的,但是这表明这次递归逆序对只有1个吗?实际不是的,3已经大于2了,已知左半边数组是升序的,那么左半边数组3之后的元素都是大于2的(4也大于2),而这个逆序的对数非常容易求出。

形成代码,发现实际上只是在归并排序的基础上加了一个全局变量和一行代码。

#include 
using namespace std;

int sum = 0;

void Merge(int a[], int low, int mid, int high)
{
	int i = low, j = mid+1;
	int k = 0;

	int *temp = (int*)malloc((high-low+1)*sizeof(int));
	if (NULL == temp)
		throw "Memory unavailable!";

	while (i<=mid && j<=high)
	{
		if (a[i] <= a[j])
			temp[k++] = a[i++];

		else
		{
			temp[k++] = a[j++];
			sum += mid - i + 1; //计算左半边数组a[i]大于右半边数组a[j]的时候逆序个数。显然左边数组i下标之后的元素也是大于右侧a[j]的。
		}
	}

	while (i<=mid)
		temp[k++] = a[i++];
	while (j<=high)
		temp[k++] = a[j++];

	for (i=low,k=0; i<=high; i++)
	{
		a[i] = temp[k++];
	}
	
	free(temp);
	temp = NULL;
}
void MergeSort(int a[], int low, int high)
{
	int mid;
	if (low < high)
	{
		mid = low + (high-low)/2;
		MergeSort(a, low, mid);
		MergeSort(a, mid+1, high);
		Merge(a, low, mid, high);
	}
}

int main()
{
	int a[] = {24,22,23,222};
	MergeSort(a, 0, 3);
	cout << sum << endl;
	return 0;
}
还有用树状数组的方法,不是很熟悉这个结构,就不写了。。

你可能感兴趣的:(数据结构与算法)