逆序对定义:对于一个包含N个非负整数的数组A[1..n],如果有i < j,且A[ i ]>A[ j ],则称(A[ i] ,A[ j] )为数组A中的一个逆序对。
常见的两种方法求解逆序对:1.穷举法(暴力求解),时间复杂度O(n^2)。2.归并法, 时间复杂度O(nlogn)。
穷举法:对于一个给定的序列,依次从左往右取每一个元素,从该元素右边第一个元素开始向右扫描,遇到比它小的元素,则计数+1,直到处理完整个序列。
#include <iostream>
#include <cstdlib>
#include <cstdio>
using namespace std;
#define MAXN 1000
int num[MAXN];
int sum = 0;
void solve(int n)
{
for(int i = 0; i < n; i++)
for(int j = i+1; j < n; j++)
{
if(num[i] > num[j])
sum++;
}
}
int main(int argc, char const *argv[])
{
int n;
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%d", &num[i]);
solve(n);
printf("%d\n", sum);
return 0;
}
归并法:将序列A[l..r]分成两半A[l..mid]和A[mid+1..r]分别进行归并排序,然后再将这两半合并起来。
在合并的过程中(设l<=i<=mid,mid+1<=j<=r),当A[i]<=A[j]时,不产生逆序数;当A[i]>A[j]时,在
前半部分中比A[i]大的数都比A[j]大,所以,将A[j]放在A[i]前面的话,逆序数要加上mid+1-i。因此,可以在归并排序中的合并过程中计算逆序数。
#include <iostream>
#include <cstdlib>
#include <cstdio>
using namespace std;
#define MAXN 1000
int num[MAXN], temp[MAXN];
int sum = 0;
void Merge(int l, int mid, int r)
{
int i = l, j = mid+1, k = l;
while(i <= mid && j <= r)
{
if(num[i] > num[j])
{
temp[k++] = num[j++];
sum += mid-i+1;//产生逆序对
}
else
temp[k++] = num[i++];
}
while(i <= mid)
temp[k++] = num[i++];
while(j <= r)
temp[k++] = num[j++];
for(int i = l; i <= r; i++)
num[i] = temp[i];
}
void Merge_sort(int l, int r)
{
if(l < r)
{
int mid = l + (r-l)/2;//可防止加法溢出
Merge_sort(l, mid);//左边
Merge_sort(mid+1, r);//右边
Merge(l, mid, r);//合并
}
}
int main(int argc, char const *argv[])
{
int n;
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%d", &num[i]);
Merge_sort(0, n-1);
printf("%d\n", sum);
return 0;
}
附上POJ逆序对的一道测试题:http://poj.org/problem?id=1804