求逆序对(inversion)的个数
2014-08-07 22:50 valerlina 阅读(3610) 评论(0) 编辑 收藏 举报2-4 逆序对
设A[1...n]是一个包含n个不同数的数组,如果在i<j的情况下,有A[i]>A[j],则(i,j)就称为A中的一个逆序对(inversion)。
a)列出数组<2, 3, 8, 6, 1>的5个逆序对
b)如果数组的元素取自集合{1,2,...,n}, 那么, 怎样的数组含有最多的逆序对?它包含多少个逆序对?
c)插入排序的运行时间与输入数组中逆序对的数量之间有怎样的关系?说明你的理由。
d)给出一个算法,它能用Θ(nlgn)的最坏情况运行时间,确定n个元素的任何排列中逆序对的个数。(提示:修改合并排序)
分析与解答:
蛮力法:对于数组A[1...n],如果要确定所有的逆序对,只需要遍历一次数组,对于以A[j]结尾的逆序只需要遍历所有1<= i <=j
若满足A[i] > a[j],则说明(i,j)是一个逆序对,否则不是。这是一种非常朴素的方法,整个算法的时间复杂度为Θ(n*n)
a) 采用类似上面的方法,以6结尾逆序对有(1,4),以1结尾的逆序对有(1,5),(2,5),(3,5),(4,5),总共为5个
b) 当整个数组完全逆序时,含有最多的逆序对,即A={n,...,2,1},这时
以n-1结尾的逆序对有1个,以n-2结尾的逆序对有2个,以此类推,以1结尾的逆序对有n-1个。
总共(n-1)+(n-2)+...1 = n(n-1)/2
c) 插入排序的运行时间与插入排序时的总的比较次数相关,而总共的比较次数与总的逆序对的个数相同。因此两者成正比
在插入排序的过程中,插入A[j]需要比较的次数和已排序的A[1...j-1]中比A[j]大的元素个数相同,这等价于逆序对的个数。
d) 合并排序是采用分治法的思想,若需要排序A[p...q],则可以对半分成A[p...r]和A[r...q],然后将这有序的两部分Merge。
而Merge的过程为Θ(n)的时间复杂度
根据主定率T(n)=2(Tn/2)+Θ(n),时间复杂度为T(n)=Θ(nlgn)。
而求整个序列中的逆序对,也可以利用分治法的思想,即
逆序对(A[p...q])= 逆序对(A[p...r])+逆序对(A[r...q])+逆序对(A[p...r], A[r...q]之间的)
结合合并排序,关键是求如何在Θ(n)的时间有效的求出A[p...r], A[r...q]之间的逆序对
因为在合并排序的Merge过程中,A[p...r]和A[r...q]已经有序,假设此时已经Merge到A[s...r]和A[t...q]。考虑接下来的一步:若从前者取出A[s],说明A[s]比后面的序列A[t...q]中的元素都小,不存在逆序对;若从后者取出A[t],则说明A[t]比前面的序列A[s...r]都小,即以t结尾的逆序对的数量为前者的剩余序列A[s...r]中元素的数量
Merge的过程中即可得到A[p...r], A[r...q]之间的逆序对的数量,时间复杂度亦为Θ(n), 由主定律总的时间复杂为 Θ(nlgn),这种方法要比朴素的方法 Θ(n*n)好很多。

1 //求逆序对个数(使用归并排序) 2 //2014年8月7日 BUAA Rena 3 4 int inversion(int* A,int p,int q) 5 { 6 int sum=0; 7 int mid=(p+q)/2; 8 if (p==q) //这个是终止条件,容易忽略 9 { 10 return 0; 11 } 12 13 sum+=inversion(A,p,mid); 14 sum+=inversion(A,mid+1,q); 15 16 // 合并思想:用两个临时空间来存储A的两个子序列,然后在求解的过程中排好序再赋值给A 17 int m=mid-p+1; 18 int n=q-mid; 19 int *B=(int*)malloc(sizeof(int)*m); 20 int *C=(int*)malloc(sizeof(int)*n); 21 int i,j; 22 for(i=0;i<m+n;i++) 23 { 24 if(i<m) 25 { 26 B[i]=A[i+p]; 27 } 28 else 29 { 30 C[i-m]=A[i+p]; 31 } 32 } 33 34 i=j=0; 35 int k=p; 36 while(i<m&&j<n) 37 { 38 if(B[i]<=C[j]) 39 A[k++]=B[i++]; 40 else 41 { 42 sum+=m-i; 43 A[k++]=C[j++]; 44 } 45 } 46 if(i==m) 47 { 48 while(j<n) 49 A[k++]=C[j++]; 50 } 51 else 52 { 53 if(j==n) 54 { 55 while(i<m) 56 A[k++]=B[i++]; 57 } 58 } 59 return sum; 60 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库