问题描述
- 考虑1,2,…,n(n<=100000)的排列 i 1 , i 2 , . . . , i n i_1,i_2,...,i_n i1,i2,...,in,如果其中存在j,k,满足 j < k j<k j<k且 i j > i k i_j>i_k ij>ik,那么就称 ( i j , i k ) (i_j,i_k) (ij,ik)是这个排列的一个逆序。
- 一个排列含有的逆序的个数成为这个排列的逆序数。例如排列263451含有8个逆序(2,1),(6,3),(6,4),(6,5),(6,1),(3,1),(4,1),(5,1),因此该排列的逆序数就是8.
- 现给定1,2,…,n的一个排列,求它的逆序数。
题目解析
- 1、笨办法: O ( n 2 ) \Omicron(n^2) O(n2)
- 2、分治
O
(
n
l
o
g
n
)
\Omicron(nlogn)
O(nlogn):
- 1)将数组分成两半,分别求出左半边的逆序数和右半边的逆序数
- 2)再算有多少逆序是由左半边取一个数和右半边取一个数构成(要求 O ( n ) \Omicron(n) O(n)实现)。
注:2)的关键:左半边和右半边都是排好序的。比如,都是从大到小排序的。这样,左右半边只需要从头到尾各扫一遍,就可以找出由两边各取一个数构成的逆序个数。
- 如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/ff978639bbb96cae386d8fe96186a681.png)
![](https://i-blog.csdnimg.cn/blog_migrate/81352bd38402e568dda09ac19bed29e0.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7564e1b468f060bd309e7db3da2a51f0.png)
![](https://i-blog.csdnimg.cn/blog_migrate/55155c119b025300b392666148837d0a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3bb6eacfa9afb825fb14a9c5ad670fac.png)
![](https://i-blog.csdnimg.cn/blog_migrate/47ef6ea11f13ac2666902ff9e4f0c5bb.png)
![](https://i-blog.csdnimg.cn/blog_migrate/79d0d0da047da51ce248fa2cfbb120df.png)
每当al[i]>ar[j]时则说明存在逆序数,逆序数的个数为ar[j]及其以后的数:len2-j
- C语言代码示例
#include<stdio.h>
int mergeSortAndCount(int a[], int left, int mid, int right);
int merge(int a[], int left, int right)
{
int mid = left + (right-left)/2;
int count = 0;
if(left < right)
{
count += merge(a, left, mid);
count += merge(a, mid+1, right);
count += mergeSortAndCount(a, left, mid, right);
}
return count;
}
int mergeSortAndCount(int a[], int left, int mid, int right)
{
int len1 = mid - left + 1;
int len2 = right - mid;
int al[len1], ar[len2], i, j, k;
int count = 0; //逆序数
for(i = 0; i < len1; i++)
al[i] = a[left+i];
for(i = 0; i < len2; i++)
ar[i] = a[mid+1+i];
i = 0;
j = 0;
for(k = left; k <= right; k++)
{
if(i >= len1)
{
a[k] = ar[j];
j++;
}else if(j >= len2)
{
a[k] = al[i];
i++;
}else if(al[i] > ar[j]){
a[k] = al[i];
i++;
count += len2-j;
}else{
a[k] = ar[j];
j++;
}
}
return count;
}
int main()
{
int a[100];
int i = 0, n;
printf("请输入数组的长度:");
scanf("%d", &n);
printf("请输入%d个整数:\n", n);
for(i=0; i< n; i++)
scanf("%d", &a[i]);
printf("逆序数为:%d\n", merge(a, 0, n-1));
return 0;
}
- 运行示例
![](https://i-blog.csdnimg.cn/blog_migrate/2356b346acd189f21d835cd17fd87b99.png)