题目
题目:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007。
输入
描述
题目保证输入的数组中没有的相同的数字
数据范围:
对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5
示例
1,2,3,4,5,6,7,0 结果是 7
思路
一看到这个题我就感觉似曾相识,知道需要使用归并排序的思想来做,却早已忘记了归并排序的思想到底是什么。。。于是特地整理了一遍,见博客归并排序算法。
在归并排序的归并步骤时,可以顺便统计逆序对的个数。
图一.png
假设正在归并上面的数组,左侧的2,3,6,8
和右侧的1,4,5,7
已经排好序了,左侧和右侧内部都没有逆序对,而从左侧取一个数,从右侧取一个数,则有可能形成逆序对。
例如,开始左侧拿出2
,右侧拿出1
,可知2>1
,形成了逆序对。此时逆序对只是加1
吗?并不是,因为2
右边的数都是大于2
的,所以可以判断左边的数和右边的1
可以形成4
对逆序对((2,1)、(3,1)、(6,1)、(8,1))。
接下来比2
和4
,不会形成逆序对。再比3
和4
,不会形成逆序对。
当比较到6
和4
的时候,形成了逆序对,个数为2
((6,4)、(8,4))。
图二.png
归纳一下,也就是在归并的时候,如果右侧的元素小于左侧的元素,这个时候开始统计逆序对就行了,如果左侧的索引为i
,左侧的末尾元素的索引为mid
,逆序对个数就为mid-i+1
。
这样并没有结束,前面的假设是左侧和右侧是有序的,事实上并不是,左侧和右侧也进行了归并的过程才能变得有序,而在归并过程中,也能计算出逆序对的个数。
所以:
总的逆序对的个数=左侧归并时求得的逆序对个数 + 右侧归并时求得的逆序对个数 + 对整体进行归并时的逆序对个数。
可能会怀疑这三种情况会有重复,但是并没有。左侧归并找到的逆序对相当于从左侧数组中取2
个数,而整体归并的时候是分别从左右数组中取1
个数,不可能发生重复!
代码
知道上面的思路后,可以很容易的将归并排序代码进行修改。
#include <iostream>
#include <stdlib.h>
using namespace std;
int const N = 6;
//下面注释掉的是标准的归并排序,供对比使用
/*void merge(int* a,int left,int mid,int right)
{
int* b=(int*)malloc(N*sizeof(int));
int i, j, k;
i = left;
j = mid+1;
k = left;
for (int i = 0; i < N; i++)
{
b[i] = a[i];
}
for (; i <= mid && j <= right; k++)
{
if (b[i] <= b[j])
{
a[k] = b[i];
i++;
}
if (b[i] > b[j])
{
a[k] = b[j];
j++;
}
}
if (i <= mid)
{
for (; i <= mid;)
{
a[k] = b[i];
i++;
k++;
}
}
if (j <= right)
{
for (; j <= right;)
{
a[k] = b[j];
j++;
k++;
}
}
free(b);
b = NULL;
return;
}
void merge_sort(int* a, int left, int right)
{
if(left<right)
{
int mid = (left + right) / 2;
merge_sort(a, left, mid);
merge_sort(a, mid+1, right);
merge(a, left, mid, right);
}
}
*/
int merge2(int* a, int left, int mid, int right)//合并有序数组
{
int* b = (int*)malloc(N * sizeof(int));
int i = left;
int j = mid + 1;
int k = left;
int count = 0;//用于计数合并时逆序对数量
for (int i = 0; i < N; i++)//拷贝一份数组,用于对比
{
b[i] = a[i];
}
for (; i <= mid && j <= right; k++)
{
if (b[i] <= b[j])
{
a[k] = b[i];
i++;
}
if (b[i] > b[j])
{
a[k] = b[j];
j++;
count += mid - i + 1;//计数合并时的逆序对数量,注意不是count+=1!具体原因在上面有详细解释
}
}
if (i <= mid)
{
for (; i <= mid;)
{
a[k] = b[i];
i++;
k++;
}
}
if (j <= right)
{
for (; j <= right;)
{
a[k] = b[j];
j++;
k++;
}
}
free(b);
b = NULL;
return count;
}
int merge_sort2(int* a, int left, int right)
{
if (left < right)
{
int res = 0;//用于记录结果
int mid = (left + right) / 2;
int lres = merge_sort2(a, left, mid);//左半边的逆序对数量
int rres = merge_sort2(a, mid + 1, right);//右半边的逆序对数量
int mres = merge2(a, left, mid, right);//合并过程中的逆序对数量
res = lres + rres + mres;//总数量是上面三者相加
return res;
}
return 0;//如果只有一个元素,则不可能产生逆序对,逆序对至少要2个元素
}
int main()
{
int a[6] = {2,3,4,5,6,1};
int res=merge_sort2(a, 0, 5);
cout << res<<endl;
}