面试题36:数组中的逆序对

1.在数组中的两个数字如果前面的一个数字大于后面的数字,则这两个数字组成一个逆序对,输入一个数组,求这个数组中的逆序对的总数。
分析:
例如在数组{7,5,6,4}中,一共存在5个逆序对,{7,6},{7,5},{7,4},{5,4},{6,4}。常规的做法是每次确定一个数字在数组中后面有多少个数比他小,这样的时间复杂度是O(n*n)。比较大,可以先将大的数组分为小的数组,再统计逆序对,看能不能较少比较的次数。

例如:第一步,将数组进行二分,分为单个元素时,先两两进行合并排序,此时的逆序数只能是1或者是0,因为只有两个数,如果是升序,则是0,如果是降序则是1;这样两个组得到的逆序数的和是2.



第二步,对两个分别包含2个元素的数组进行排序并统计逆序数。两个指针分别指向数组的末尾,因为两个子数组已经是已排序的数组,这样的话如果第一个数组的末尾的元素大于第二个数组的末尾元素的话,则他肯定比第二个数组中的所有的元素都大,例如7>6,则7肯定是最大的数,放在末尾,逆序数是第二数组的长度;比较5的时候,5<6,此时带比较的数中最大的数就是6了,不存在逆序数。拷贝到辅助数组中,继续比较剩余的数。最后得到逆序数是5.整个过程就是归并排序的过程。


源码:

/**
		* 功能说明:数组中逆序数的统计
		* 作者:K0713
		* 日期:2016-9-7
		**/
#include<iostream>
using namespace std;
int InversePairs(int* data, int length);
int InversePairsCore(int* data, int* copy, int start, int end);
int main()
{
	int data[] = { 1, 2, 3, 4, 7, 6, 5 };
	int length = sizeof(data) / sizeof(int);
	int count = InversePairs(data, length);
	cout << "the count is: " << count<<endl;
	system("PAUSE");
	return 0;
}
int InversePairs(int* data, int length)
{
	if (data == NULL || length < 0)
		return 0;
	int* copy = new int[length];//辅助空间
	for (int i = 0; i < length; ++i)
		copy[i] = data[i];
	int count = InversePairsCore(data, copy, 0, length - 1);
	delete[] copy;
	return count;
}
int InversePairsCore(int* data, int* copy, int start, int end)
{
	if (start == end)
	{
		copy[start] = data[start];
		return 0;
	}
	int length = (end - start) / 2;
	int left = InversePairsCore(copy, data, start, start + length);
	int right = InversePairsCore(copy, data, start + length + 1, end);
	// i初始化为前半段最后一个数字的下标
	int i = start + length;
	// j初始化为后半段最后一个数字的下标
	int j = end;
	int indexCopy = end;
	int count = 0;
	while (i >= start && j >= start + length + 1)
	{
		if (data[i] > data[j])//前一部分的当前值大于后部分的值,后面部分的值前面有多少数就有多少对逆序数
		{
			copy[indexCopy--] = data[i--];//从尾部开始更新copy的值,每一次都是找到次大的值
			count += j - start - length;
		}
		else
		{
			copy[indexCopy--] = data[j--];
		}
	}
	for (; i >= start; --i)//将前面部分剩余的元素考到辅助数组中
		copy[indexCopy--] = data[i];
	for (; j >= start + length + 1; --j)
		copy[indexCopy--] = data[j];
	return left + right + count;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值