分治法求一个N个元素数组的逆序数

时间:2021-02-27 11:39:35

背景

 逆序数:也就是说,对于n个不同的元素,先规定各元素之间有一个标准次序(例如n个 不同的自然数,可规定从小到大为标准次序),于是在这n个元素的任一排列中,当某两个元素的先后次序与标准次序不同时,就说有1个逆序。一个排列中所有逆序总数叫做这个排列的逆序数。

定义

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。逆序数为偶数的排列称为偶排列;逆序数为奇数的排列称为奇排列。如2431中,21,43,41,31是逆序,逆序数是4,为偶排列。

问题求解

分治法求逆序数相当于在归并排序的过程中加上相应的逆序数的数目。

假设数组A被划分成前半部分(A[left]->A[mid]),后半部分(A[mid+1],A[right])

假设前半部分与后半部分各自都是从小到大排好序的,若A[left]<A[mid+1]那么由A[mid+1]这个数与数组前半部分造成的逆序数目是mid-left+1,这个数在接下来的逆序数中不用再考虑,反之类同。

使用分治法解决该问题的时间复杂度为O(N*log(N)).

代码如下:

 #include<iostream>
#include<conio.h>
using namespace std;
int merge(int *A,int left,int mid,int right){
int *temp=new int[right-left+];
int num=;
int i=left;
int j=mid+;
int k;
int index=;
for(;i<=mid&&j<=right;){
if(A[i]>A[j]){
num+=mid-i+;
temp[index]=A[j];
j++;
}
else{
temp[index]=A[i];
i++;
}
index++;
}
//i=mid的时候,A[i]位置的数还未填充到数组temp中
//因此判断条件包含等于号
if(i<=mid)
for(;i<=mid;i++){
temp[index]=A[i];
index++;
}
if(j<=right)
for(;j<right;j++){
temp[index]=A[j];
index++;
}
for(k=;k<right-left+;k++)
A[left+k]=temp[k]; delete []temp;
return num;
}
int inversion(int *A,int left,int right){
if(left>=right)
return ;
int mid=(left+right)/;
int num1=inversion(A,left,mid);
int num2=inversion(A,mid+,right);
return num1+num2+merge(A,left,mid,right);
}
int main()
{
int A[]={,,,,};
cout<<inversion(A,,)<<endl;
_getch();
}