算法导论 — 思考题4-4 逆序对

逆序对)假设A[1..n]A[1..n]是一个有nn个不同数的数组。若i<ji < jA[i]>A[j]A[i] > A[j],则对偶(i,j)(i, j)称为A的一个逆序对(inversion)。
  a. 列出数组<2,3,8,6,1><2, 3, 8, 6, 1>的5个逆序对。
  b. 由集合1,2,,n{1, 2, …, n}中的元素构成的什么数组具有最多的逆序对?它有多少逆序对?
  c. 插入排序的运行时间与输入数组中逆序对的数量之间是什么关系?证明你的回答。
  d. 给出一个确定在nn个元素的任何排列中逆序对数量的算法,最坏情况需要Θ(nlgn)Θ(n{\rm lg}n)时间。(提示:修改归并排序)
  
  a.
  5个逆序对:(8,6)(6,1)(8,1)(3,1)(2,1)(8, 6) (6, 1) (8, 1) (3, 1) (2, 1)
  
  b.
  逆序对最多的情况出现在元素按降序排列的时候,即n,n1,,2,1{n, n-1, … , 2, 1}。这种情况下,任意两个元素都构成逆序对,一共有Cn2=(n(n1))/2C_n^2=(n(n-1))/2组逆序对。

c.
  插入排序的运行时间T(n)T(n)与数组中的逆序对数量R(n)R(n)成线性关系,即T(n)=Θ(R(n))T(n) = Θ(R(n))。下面用数学归纳法证明。
  初始情况n=2n = 2,线性关系显然成立。
  下面进入归纳过程。假设对于n1n-1个元素的情况,插入排序的运行时间T(n1)T(n-1)与逆序对的数量R(n1)R(n-1)成线性关系T(n1)=Θ(R(n1))T(n-1) = Θ(R(n-1))。对于nn个元素的情况,其逆序对数目R(n)R(n)分为两部分:
  ① 前n1n-1个元素的逆序对数目R(n1)R(n-1)
  ② 前n1n-1个元素与第nn个元素构成的逆序对数目Rp(n)R_p(n)
  故R(n)=R(n1)+Rp(n)R(n) = R(n-1) + R_p(n)
  nn个元素的插入排序可分为两步进行:
  ① 首先将前n1n-1个元素排序,其时间T(n1)=Θ(R(n1))T(n-1) = Θ(R(n-1))
  ② 然后将第nn个元素插入到已排序好的前n1n-1个元素中。前n1n-1个元素与第nn个元素存在Rp(n)R_p(n)个逆序对,这说明前n1n-1个元素中一共存在Rp(n)R_p(n)个元素比第nn个元素要大。插入排序在这一阶段要将第nn个元素插入到合适的位置,这一步的运行时间Tp(n)=Θ(Rp(n))T_p(n) = Θ(R_p(n))
  故排序nn个元素的运行时间T(n)=T(n1)+Tp(n)=Θ(R(n1))+Θ(Rp(n))=Θ(R(n1)+Rp(n))=Θ(R(n))T(n) = T(n-1) + T_p(n) = Θ(R(n-1)) + Θ(R_p(n)) = Θ(R(n-1) + R_p(n)) = Θ(R(n))
  综上所述,插入排序的运行时间T(n)T(n)与数组中的逆序对数量R(n)R(n)成线性关系T(n)=Θ(R(n))T(n) = Θ(R(n))

d.
  采用分治法。对于数组A[1..n]A[1 .. n],将其对半分为两个子数组:AL=A[1..n/2]A_L = A[1 .. ⌊n/2⌋]AR=A[n/2+1..n]A_R = A[⌊n/2⌋+1 .. n]A[1..n]A[1 .. n]的逆序对数目R(A)R(A)分为33部分:
  ① 左半边子数组ALA_L的逆序对数目R(AL)R(A_L)
  ② 右半边子数组ARA_R的逆序对数目R(AR)R(A_R)
  ③ 左半边子数组的元素与右半边子数组的元素构与的逆序对数目R(AL,AR)R(A_L, A_R)
  故R(A)=R(AL)+R(AR)+R(AL,AR)R(A) = R(A_L) + R(A_R) + R(A_L, A_R)
  下面考虑③。一种简单的方法是,对ALA_L中的每个元素AL[i]A_L[i],遍历ARA_R中的每个元素AR[j]A_R[j],寻找与AL[i]A_L[i]构成逆序对的元素。这种方法的时间为Θ((n/2)2)=Θ(n2)Θ((n/2)^2) = Θ(n^2),时间成本太高。然而,如果ALA_LARA_R分别已经排好序,可以把时间缩减到Θ(n)Θ(n)。并且,分别排序ALA_LARA_R并不影响ALA_LARA_R之间的逆序对数目R(AL,AR)R(A_L, A_R)。这一过程具体如下:
  算法导论 — 思考题4-4 逆序对
  上述方法可以和归并排序揉合在一起,先执行CROSSINVERSIONCOUNT(A,p,q,r){\rm CROSS-INVERSION-COUNT}(A, p, q, r),然后执行归并排序的MERGE{\rm MERGE}过程。在递归的每一层,执行CROSSINVERSIONCOUNT(A,p,q,r){\rm CROSS-INVERSION-COUNT}(A, p, q, r)MERGE{\rm MERGE}过程各花费Θ(n)Θ(n)时间。递归一共有Θ(lgn)Θ({\rm lg}n)层,因此该算法的执行时间为Θ(nlgn)Θ(n{\rm lg}n)。下面给出完整的伪代码。
  算法导论 — 思考题4-4 逆序对
  代码链接:https://github.com/yangtzhou2012/Introduction_to_Algorithms_3rd/tree/master/Chapter02/Problem_2-4/InversionCount