算法导论 逆序对问题

来源:互联网 发布:安装ubuntu服务器版 编辑:程序博客网 时间:2024/05/01 14:15
 

先是逆序对的定义:一个n个互异元素的数组a,求满足i<j时a[i]>a[j]条件的数对个数。

数据输入:n(元素个数),a数组

数据输出:逆序对个数

算法分析:这个题目十分的经典,是归并排序的一个完美应用,分治是其主要思想,具体可以概括假设f(i,j)为i到j号元素中的逆序对个数,取一个分割点k,假设s(i,j,k)表示以k为分割点,第一个元素在i到k中,第二个元素在k+1到j中形成的逆序对数。那么我们就得到一个递归式:f(i,j)=f(i,k)+f(k+1,j)+s(i,j,k)。很自然的与归并排序联系到了一起,对于更小规模的f可以递归求解,如果对于a数组的i到k以及k+1到j两个部分元素均为有序的情况,那么对于当a[j]<a[i]的情况,必然有a[j]<a[i..k],即a[j]和i到k号元素都形成逆序对,此时只要将s(i,j,k)加上k-i+1就可以了(i到k之间的元素个数),这个过程很像归并排序的Merge的过程——比较当前i和j的状态并放入较小的。于是我们就得到了算法,和归并排序一起操作:外围设置一个计数器cnt,每次归并过程分为分割与合并两个部分,分割照常处理,在合并部分中的if (a[i]>a[j]) b[++l]=a[++j];中加入一个计数过程,即cnt+=k-i+1。这样当排序完成后,统计也就完成了。

算法复杂度分析:时间上因为是和归并排序一起处理,所以时间复杂度应该是O(nlogn),同时因为是归并排序,所以还附加了O(n)的空间复杂度。

源代码如下:

#include<iostream>
using namespace std;
static int count=0;
void  merge(int a[],int p,int q,int r)
{
 int n1=q-p+1;
 int n2=r-q;
 int * l=new int[n1+1];
 int *  rl=new int[n2+1];
 for(int i=0;i<n1;i++)
  l[i]=a[p+i-1];
 for(int j=0;j<n2;j++)
  rl[j]=a[q+j];
 l[n1]=65535;
 rl[n2]=65535;
 i=0;
 j=0;
 for(int k=p-1;k<r;k++)
 {
  if(l[i]<=rl[j])
  {
   a[k]=l[i];
   i++;
  }
  else
  {
   a[k]=rl[j];
   j++;
   count+=(q-i+1-p);
  }

 }
}
void mergeSort(int a[],int p,int r)
{
 if(p<r)
 {
  int q=(p+r)/2;
  mergeSort(a,p,q);
  mergeSort(a,q+1,r);
  merge(a,p,q,r);
 }
}
int main()
{

 int a[7]={6,5,4,3,2,1,0};
 mergeSort(a,1,7);
 for(int i=0;i<7;i++)
  cout<<a[i];
 cout<<endl;
 cout<<count<<endl;
 return 0;
}

 

 

原创粉丝点击