任务描述
本关任务:输入一个数组,求出这个数组中的逆序对的总数。保证输入的数组中没有的相同的数字。
相关知识
一个排列含有逆序的个数称为这个排列的逆序数。在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。
例如,排列2,6,3,4,5,1中含有8个逆序(2,1),(6,3),(6,4),(6,5),(6,1),(3,1),(4,1),(5,1),因此该排列的逆序数就是8。 显然,由1,2,…,n 构成的所有n!个排列中,排列1,2,…,n的逆序数是0;排列n,(n-1),…,2,1的逆序数最大,为n(n-1)/2,逆序数越大的排列与原始排列的差异度就越大。
现给定1,2,…,n的一个排列,求它的逆序数。
求排列的逆序数有两种解法枚举和分治,时间复杂度分别为O(n2)
和O(n∗logn)
。在此我们利用分治思想来解。 思路: 类似于归并排序:
- 将数组分成两半,分别求出左半边的逆序数和右半边的逆序数
- 再算有多少逆序是由左半边取一个数和右半边取一个数构成
实际上就是归并排序的改造,即对此排列进行归并排序,边排序边计算它的逆序数。
求逆序数的关键: 设左半边和右半边都是从小到大有序的(此时进行归并),从左到右依次扫描,比较两个各取一个数的大小,如果左边第一个数比右边第一个数大,那么左边排列的其他数字也可以和右边第一个数构成逆序数,以此类推。
编程要求
根据提示,在右侧编辑器补充代码,计算排列的逆序数并输出结果。
测试说明
平台会对你编写的代码进行测试:
测试输入: 9
9 6 8 2 1 3 4 5 7
预期输出: 9,6,8,2,1,3,4,5,7,
逆序数的个数为:20
#include<cstdio>
#include <iostream>
using namespace std;
long sum = 0;//逆序数的个数
void output(int *a,int n) ;
void swap(int &a, int &b) ;
void Merge(int a[],int s,int m, int e) ;
void MergeSort(int a[],int s,int e) ; //归并排序
void MergeAndCountNum(int * arr,int s,int mid,int e); //归并有序序列并计算逆序数的个数
int main()
{
int i,n,x;
int *a=0,*b=0;
scanf("%d",&n);
a=new int[n];
b=new int[n];
for(i=0;i<n;i++)
{
scanf("%d",&x);
a[i]=b[i]=x;
}
output(a,n);
MergeSort(a,0,n-1);
printf("逆序数的个数为:");
cout << sum ;
delete []a;
delete []b;
system("pause");
return 0;
}
void output(int *a,int n)
{
int i;
for(i=0;i<n;i++)
{
printf("%d,",a[i]);
}
printf("\n");
}
void swap(int &a, int &b)
{
int t;
t=a;a=b;b=t;
}
void Merge(int a[],int s,int m, int e)
{ //将数组a的局部a[s,m]和a[m+1,e]合并到tmp,并保证tmp有序,然后再拷贝回a[s,e] //归并操作时间复杂度:O(e-m+1),即O(n)
int pb = 0;
int p1 = s,p2 = m+1;
int *tmp = new int[e-s+1];
/********** Begin **********/
while( p1 <= m && p2 <= e)
{ if( a[p1] < a[p2])
tmp[pb++] = a[p1++];
else
tmp[pb++] = a[p2++];
}
while( p1 <= m)
tmp[pb++] = a[p1++];
while( p2 <= e)
tmp[pb++] = a[p2++];
for(int i = 0;i < e-s+1; ++i)
a[s+i] = tmp[i];
/********** End **********/
delete []tmp;
}
void MergeSort(int a[],int s,int e)
{
int *tmp = new int[e-s+1];
if( s < e)
{ int m = s + (e-s)/2;
/********** Begin **********/
MergeSort(a,s,m);
MergeSort(a,m+1,e);
MergeAndCountNum(a,s,m,e);
/********** End **********/
}
delete []tmp;
}
void MergeAndCountNum(int * arr,int s,int mid,int e)
{
/********** Begin **********/
int *tmp = new int[e-s+1];
int pb = 0;
int p1 = s, p2 = mid + 1;
while (p1 <= mid && p2 <= e) {
if (arr[p1] > arr[p2]) {
tmp[pb++] = arr[p2++];
sum += mid - p1 + 1; // 计算逆序数的个数
} else {
tmp[pb++] = arr[p1++];
}
}
while (p1 <= mid) {
tmp[pb++] = arr[p1++];
}
while (p2 <= e) {
tmp[pb++] = arr[p2++];
}
for (int i = 0; i < e - s + 1; ++i) {
arr[s + i] = tmp[i];
}
delete[] tmp;
/********** End **********/
}