求逆序对算法总结
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)
源码如下:
其时间复杂度比较的高。一般不值得使用,
可以根据分析插入排序的特点,重新设计,插入排序,每个数字向前移动的次数,就是以该元素为第一元素的逆序对的数目,其时间复杂度和插入排序是一样的O(n^2),具体的源代码如下所示(直接来自于网络):
此种方法充分利用到了所学到的排序只是,但是时间复杂度依旧很大,于是很多人又想出了另一种方案,依据归并排序的特点就行求解,源代码(直接来源于网络)如下:
此外,还有气的解法,有人提出树状数组的解法,还有二分加快排的解法,自己去看吧,先说这么多了。
程序示例,所有的源代码(VS2010编译):
例如,数组(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
相关文章推荐
- 求逆序对算法总结
- 对一道“写一个算法实现字符串逆序存储,要求不另设串存储空间.”题目的总结!
- 算法-蓝桥杯-算法提高 逆序排列(JAVA)
- 算法总结(0)目录式概括
- 算法分析结课总结--回溯算法
- 面试题总结之MISC(操作系统,网络,算法和数据结构,数学,面向对象设计,软件开发,测试,工具)
- JVM调优总结(2):基本垃圾回收算法
- QEMU的AddrRange地址空间对象模型算法总结(QEMU2.0.0)
- 网络流算法与建模总结
- 算法的时间复杂度和空间复杂度-总结
- 海量数据处理 算法总结
- 数据结构与算法——排序算法总结(1)
- 几种常见模式识别算法整理和总结
- 机器学习笔记(8)---K-近邻算法(6)---KNN算法学习总结
- 几种常见模式识别算法整理和总结
- 无监督学习算法K-means算法总结与c++编程实现
- JVM调优总结(三)-基本垃圾回收算法
- 字符串匹配算法总结
- 几种常见模式识别算法整理和总结
- 总结几种常用的安全算法