(逆序对)假设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. 给出一个确定在n个元素的任何排列中逆序对数量的算法,最坏情况需要Θ(nlgn)时间。(提示:修改归并排序)
解
a.
5个逆序对:(8,6)(6,1)(8,1)(3,1)(2,1)
b.
逆序对最多的情况出现在元素按降序排列的时候,即n,n−1,…,2,1。这种情况下,任意两个元素都构成逆序对,一共有Cn2=(n(n−1))/2组逆序对。
c.
插入排序的运行时间T(n)与数组中的逆序对数量R(n)成线性关系,即T(n)=Θ(R(n))。下面用数学归纳法证明。
初始情况n=2,线性关系显然成立。
下面进入归纳过程。假设对于n−1个元素的情况,插入排序的运行时间T(n−1)与逆序对的数量R(n−1)成线性关系T(n−1)=Θ(R(n−1))。对于n个元素的情况,其逆序对数目R(n)分为两部分:
① 前n−1个元素的逆序对数目R(n−1);
② 前n−1个元素与第n个元素构成的逆序对数目Rp(n)。
故R(n)=R(n−1)+Rp(n)。
n个元素的插入排序可分为两步进行:
① 首先将前n−1个元素排序,其时间T(n−1)=Θ(R(n−1));
② 然后将第n个元素插入到已排序好的前n−1个元素中。前n−1个元素与第n个元素存在Rp(n)个逆序对,这说明前n−1个元素中一共存在Rp(n)个元素比第n个元素要大。插入排序在这一阶段要将第n个元素插入到合适的位置,这一步的运行时间Tp(n)=Θ(Rp(n))。
故排序n个元素的运行时间T(n)=T(n−1)+Tp(n)=Θ(R(n−1))+Θ(Rp(n))=Θ(R(n−1)+Rp(n))=Θ(R(n))。
综上所述,插入排序的运行时间T(n)与数组中的逆序对数量R(n)成线性关系T(n)=Θ(R(n))。
d.
采用分治法。对于数组A[1..n],将其对半分为两个子数组:AL=A[1..⌊n/2⌋]和AR=A[⌊n/2⌋+1..n]。A[1..n]的逆序对数目R(A)分为3部分:
① 左半边子数组AL的逆序对数目R(AL)
② 右半边子数组AR的逆序对数目R(AR)
③ 左半边子数组的元素与右半边子数组的元素构与的逆序对数目R(AL,AR)。
故R(A)=R(AL)+R(AR)+R(AL,AR)。
下面考虑③。一种简单的方法是,对AL中的每个元素AL[i],遍历AR中的每个元素AR[j],寻找与AL[i]构成逆序对的元素。这种方法的时间为Θ((n/2)2)=Θ(n2),时间成本太高。然而,如果AL和AR分别已经排好序,可以把时间缩减到Θ(n)。并且,分别排序AL和AR并不影响AL和AR之间的逆序对数目R(AL,AR)。这一过程具体如下:
上述方法可以和归并排序揉合在一起,先执行CROSS−INVERSION−COUNT(A,p,q,r),然后执行归并排序的MERGE过程。在递归的每一层,执行CROSS−INVERSION−COUNT(A,p,q,r)和MERGE过程各花费Θ(n)时间。递归一共有Θ(lgn)层,因此该算法的执行时间为Θ(nlgn)。下面给出完整的伪代码。
代码链接:https://github.com/yangtzhou2012/Introduction_to_Algorithms_3rd/tree/master/Chapter02/Problem_2-4/InversionCount