有一个由N个实数构成的数组,如果一对元素A[i]和A[j]是倒序的,即i<j但是A[i]>A[j]则称它们是一个倒置,设计一个计算该数组中所有倒置数量的算法。要求算法复杂度为O(nlogn)
暴力求解
思路:
循环从数组中取出一个元素k,然后从k之后的元素中找到比k小的元素个数,最后统计所有的个数即为排列中逆序对的数目。
时间复杂度: O(n^2)
修改归并排序
前提知识:归并排序
归并排序是分治算法一个重要的应用。
讲解及代码:https://www.cnblogs.com/skywang12345/p/3602369.html#a42
思路:
根据归并排序的基本思想,我们来到计算逆序对。
在归并排序的合并操作时,数组被划分左右两部分,此时假设左部分的逆序对有a个 ,右边有b个,如果左边存在一个数a[i]大于右边的数a[j],由于两数组分别是有序的,那么从a[i]到左边结束部分的数字都比a[j]大,即与b[j]有关的逆序对数=(左边部分的end)-i+1 个。
那么我们只需要在每次合并操作时统计这部分个数并求和,仅在merge函数内加一条语句。
(ps:左右两部分内部的分别排序不会影响两部分之间的逆序对数,因为只是内部有序。)
代码:
#include <iostream>
using namespace std;
void merge(int a[],int start,int mid,int end,int &num){
int *tmp=new int[end-start+1];
int s=start;
int m=mid+1;
int e=end;
int k=0;
while(s<=mid && m<=e){
if(a[s]>a[m]){
tmp[k++]=a[m++];
num+=mid-s+1; //逆序数增加 end- i + 1个
}
else{
tmp[k++]=a[s++];
}
}
while(s<=mid)
tmp[k++]=a[s++];
while(m<=e)
tmp[k++]=a[m++];
for (int i = 0; i < k; i++)
a[start + i] = tmp[i];
}
void mergeSort(int a[],int start,int end,int &num){
if(start==end)
return;
int mid=(start+end)/2;
mergeSort(a,start,mid,num);
mergeSort(a,mid+1,end,num);
merge(a,start,mid,end,num);
}
int main(){
int a[]={8,3,2,9,7,1,5,4};
int num=0; //存储逆序数组对数
mergeSort(a,0,7,num); //7是数组a的长度
cout<<num<<endl;
return 0;
}
时间复杂度:O(nlog n)