您的位置:首页 > 其它

求逆序对算法总结

2013-05-11 10:11 471 查看
设A[1..n]是一个包含N个非负整数的数组。如果在i〈j的情况下,有A〉A[j],则(i,j)就称为A中的一个逆序对。
例如,数组(3,1,4,5,2)的“逆序对”有<3,1>,<3,2><4,2><5,2>,共4个。
那么该如何求出给定一个数列包含逆序对个数?

首先最简单的方法,直接遍历,时间复杂度为O(n^2)

源码如下:

//最简单的办法,直接枚举遍历


intcount_inverse_pairs(inta[],intfirst,intlast)


{


intcount=0;


inti;


while(first<last)


{


i=first+1;


//intcout_tmp=0;


while(i<=last)


{


if(a[i]<a[first]){


count++;


//++cout_tmp;


}


i++;


}


//cout<<cout_tmp<<endl;


first++;


}


cout<<count<<endl;


returncount;


}


其时间复杂度比较的高。一般不值得使用,

可以根据分析插入排序的特点,重新设计,插入排序,每个数字向前移动的次数,就是以该元素为第一元素的逆序对的数目,其时间复杂度和插入排序是一样的O(n^2),具体的源代码如下所示(直接来自于网络):

/*************************************************


直接利用插入排序求逆序对,时间复杂度O(N^2)


Function:InsertionSort_Inversion


Description:对elems进行从小到大的排序来计算逆序对数量


Input:elems:ElementType类型的数组


size:elems的大小


Output:逆序对的数量


*************************************************/


intInsertionSort_Inversion(ElementTypeelems[],intsize)


{


intinversion=0;


for(inti=1;i<size;i++)


{


//inttmp_count=0;


intj=i-1;//j表示正在和key比较大小的数组元素的序号


ElementTypekey=elems[i];


while(key<elems[j]&&j>=0)


{


elems[j+1]=elems[j];


j--;


inversion++;


//++tmp_count;


}


//cout<<tmp_count<<endl;


elems[j+1]=key;


}


returninversion;


}


此种方法充分利用到了所学到的排序只是,但是时间复杂度依旧很大,于是很多人又想出了另一种方案,依据归并排序的特点就行求解,源代码(直接来源于网络)如下:

/*************************************************


利用归并排序求解


Function:MergeSort_Inversion


Description:对elems进行从小到大的排序来计算逆序对数量


Input:elems:ElementType类型的数组


begin:elems的开始序号


end:elems的结束序号


Output:逆序对的数量


*************************************************/


intMergeSort_Inversion(ElementTypeelems[],intbegin,intend)


{


intinversion=0;


if(end==begin)


{


return0;


}


elseif(end==begin+1)


{


if(elems[begin]>elems[end])


{


ElementTypetempElement=elems[begin];


elems[begin]=elems[end];


elems[end]=tempElement;


inversion++;


}


}


else


{


inversion+=MergeSort_Inversion(elems,begin,(begin+end)/2);


inversion+=MergeSort_Inversion(elems,(begin+end)/2+1,end);


inversion+=Merge_Inversion(elems,begin,(begin+end)/2,end);


}


returninversion;


}


 


 


/*************************************************


Function:Merge_Inversion


Description:对elems中begin到middle和middle到end两部分进行从小到大的合并,同时计算逆序对数量


Input:elems:ElementType类型的数组


begin:elems的开始序号


middle:elems的分割序号


end:elems的结束序号


Output:逆序对的数量


*************************************************/


intMerge_Inversion(ElementTypeelems[],intbegin,intmiddle,intend)


{


intinversion=0;


int*tempElems=newint[end-begin+1];


intpa=begin;//第一个子数组的开始序号


intpb=middle+1;//第二个子数组的开始序号


intptemp=0;//临时数组的开始序号


 


while(pa<=middle&&pb<=end)


{


if(elems[pa]>elems[pb])


{


tempElems[ptemp++]=elems[pb++];


inversion+=(middle+1-pa);


}


else


{


tempElems[ptemp++]=elems[pa++];


}


}




if(pa>middle)


{


for(;pb<=end;pb++)


{


tempElems[ptemp++]=elems[pb];


}


}


elseif(pb>end)


{


for(;pa<=middle;pa++)


{


tempElems[ptemp++]=elems[pa];


}


}


//copytempElemstolems


for(inti=0;i<end-begin+1;i++)


{


elems[begin+i]=tempElems[i];


}


delete[]tempElems;


returninversion;


}


此外,还有气的解法,有人提出树状数组的解法,还有二分加快排的解法,自己去看吧,先说这么多了。

程序示例,所有的源代码(VS2010编译):

//code-summary.cpp:定义控制台应用程序的入口点。


 


/**************************************************************************


*Copyright(c)2013,Allrightsreserved.


*文件名称:code-summary.cpp


*文件标识:


*摘要:求逆序对个数的算法


*


*当前版本:Ver1.0


*作者:徐冬冬


*完成日期:2013/05/10


*


*取代版本:


*原作者:


*完成日期:


*开放版权:GNUGeneralPublicLicenseGPLv3


*************************************************************************/


#include"stdafx.h"


 


#include<iostream>


#include<random>


#include<stdlib.h>


usingnamespacestd;


 


typedefintElementType;


 


intInsertionSort_Inversion(ElementTypeelems[],intsize);


intMergeSort_Inversion(ElementTypeelems[],intbegin,intend);


intMerge_Inversion(ElementTypeelems[],intbegin,intmiddle,intend);


 


//求逆序对个数


//初始化数组


voidinit(inta[],intlen)


{


inti=0;


for(i=0;i<len;i++)


{


a[i]=rand();


}


return;


}


///遍历数组,打印输出


voidprint(int*a,intlen)


{


inti=0;


for(i=0;i<len;i++)


{


cout<<a[i]<<'\t';


}


cout<<endl;


return;


}


//交换两个元素


voidswap(int&a,int&b)


{


inttmp=a;


a=b;


b=tmp;


return;


}


//最简单的办法,直接枚举遍历


intcount_inverse_pairs(inta[],intfirst,intlast)


{


intcount=0;


inti;


while(first<last)


{


i=first+1;


//intcout_tmp=0;


while(i<=last)


{


if(a[i]<a[first]){


count++;


//++cout_tmp;


}


i++;


}


//cout<<cout_tmp<<endl;


first++;


}


cout<<count<<endl;


returncount;


}


 


/*************************************************


直接利用插入排序求逆序对,时间复杂度O(N^2)


Function:InsertionSort_Inversion


Description:对elems进行从小到大的排序来计算逆序对数量


Input:elems:ElementType类型的数组


size:elems的大小


Output:逆序对的数量


*************************************************/


intInsertionSort_Inversion(ElementTypeelems[],intsize)


{


intinversion=0;


for(inti=1;i<size;i++)


{


//inttmp_count=0;


intj=i-1;//j表示正在和key比较大小的数组元素的序号


ElementTypekey=elems[i];


while(key<elems[j]&&j>=0)


{


elems[j+1]=elems[j];


j--;


inversion++;


//++tmp_count;


}


//cout<<tmp_count<<endl;


elems[j+1]=key;


}


returninversion;


}


 


/*************************************************


利用归并排序求解


Function:MergeSort_Inversion


Description:对elems进行从小到大的排序来计算逆序对数量


Input:elems:ElementType类型的数组


begin:elems的开始序号


end:elems的结束序号


Output:逆序对的数量


*************************************************/


intMergeSort_Inversion(ElementTypeelems[],intbegin,intend)


{


intinversion=0;


if(end==begin)


{


return0;


}


elseif(end==begin+1)


{


if(elems[begin]>elems[end])


{


ElementTypetempElement=elems[begin];


elems[begin]=elems[end];


elems[end]=tempElement;


inversion++;


}


}


else


{


inversion+=MergeSort_Inversion(elems,begin,(begin+end)/2);


inversion+=MergeSort_Inversion(elems,(begin+end)/2+1,end);


inversion+=Merge_Inversion(elems,begin,(begin+end)/2,end);


}


returninversion;


}


 


 


/*************************************************


Function:Merge_Inversion


Description:对elems中begin到middle和middle到end两部分进行从小到大的合并,同时计算逆序对数量


Input:elems:ElementType类型的数组


begin:elems的开始序号


middle:elems的分割序号


end:elems的结束序号


Output:逆序对的数量


*************************************************/


intMerge_Inversion(ElementTypeelems[],intbegin,intmiddle,intend)


{


intinversion=0;


int*tempElems=newint[end-begin+1];


intpa=begin;//第一个子数组的开始序号


intpb=middle+1;//第二个子数组的开始序号


intptemp=0;//临时数组的开始序号


 


while(pa<=middle&&pb<=end)


{


if(elems[pa]>elems[pb])


{


tempElems[ptemp++]=elems[pb++];


inversion+=(middle+1-pa);


}


else


{


tempElems[ptemp++]=elems[pa++];


}


}




if(pa>middle)


{


for(;pb<=end;pb++)


{


tempElems[ptemp++]=elems[pb];


}


}


elseif(pb>end)


{


for(;pa<=middle;pa++)


{


tempElems[ptemp++]=elems[pa];


}


}


//copytempElemstolems


for(inti=0;i<end-begin+1;i++)


{


elems[begin+i]=tempElems[i];


}


delete[]tempElems;


returninversion;


}


 


 


int_tmain(intargc,_TCHAR*argv[])


{


int*arr=newint[10];


init(arr,10);


print(arr,10);


intcount=count_inverse_pairs(arr,0,9);


print(arr,10);


 


print(arr,10);


count=InsertionSort_Inversion(arr,10);


cout<<count<<endl;


print(arr,10);


 


print(arr,10);


count=MergeSort_Inversion(arr,0,9);


cout<<count<<endl;


print(arr,10);


 


system("pause");


return0;


}


 


 


/************************************************************************/


/**


树状数组求解,没看到懂


/article/1816975.html




其实这些都用不上,二分加快排最简单,时间复杂度比树状数组小得多。


详见以下:


programliujiu;


varn,m:longint;


w,s,f,pai:array[0..100010]ofint64;


ans:int64;




procedureinit;


vari:longint;


begin


assign(input,'snooker.in');


assign(output,'snooker.out');


reset(input);


rewrite(output);


fillchar(w,sizeof(w),0);


fillchar(s,sizeof(s),0);


fillchar(f,sizeof(f),0);


randomize;


s[0]:=0;


readln(n,m);


fori:=1tondo


begin


read(w[i]);


w[i]:=w[i]-m;


s[i]:=s[i-1]+w[i];


end;


end;




proceduresort(l,r:longint);


vari,j,k,le:longint;


begin


i:=l;


j:=r;


k:=pai[random(r-l)+l];


whilei<=jdo


begin


whilepai[i]<kdoinc(i);


whilepai[j]>kdodec(j);


ifi<=jthen


begin


le:=pai[i];


pai[i]:=pai[j];


pai[j]:=le;


inc(i);


dec(j);


end;


end;


ifi<rthensort(i,r);


ifj>lthensort(l,j);


end;






procedurework(left,right:longint);


vari,t,j,mid:longint;


begin


mid:=(left+right)div2;


fori:=lefttorightdopai[i]:=s[i];


sort(left,mid);


sort(mid+1,right);


i:=left;


j:=mid+1;


while(j<=right)and(i<=mid)do


begin


while(pai[j]>pai[i])and(j<=right)


and(i<=mid)do


begin


inc(ans,(right-j+1));


inc(i);


end;


inc(j);


end;


ifleft<midthenwork(left,mid);


ifmid+1<rightthenwork(mid+1,right);


end;




proceduremain;


vari,j,k,l:longint;


begin


ans:=0;


work(0,n);


writeln(ans);


close(input);


close(output);


end;




begin


init;


main;


end.


'target='_blank'>http://tieba.baidu.com/p/934583490[/code]
*/


/************************************************************************/


 

参考:

http://www.cnblogs.com/XiongHarry/archive/2008/11/06/1327732.html

http://blog.csdn.net/fanhj__2007/article/details/5544526

http://blog.sina.com.cn/s/blog_69dcc82d0100vnff.html

树状数组实现的网络参考(我自己还没有弄懂);

http://www.cppblog.com/acronix/archive/2010/10/29/131753.aspx?opt=admin

/article/1816975.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: