(逆序对)假设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个逆序对分别为<2, 1>, <3, 1>, <8, 6>, <8, 1>, <6, 1>
b 解答:
如果集合{1, 2, ..., n}有最多的逆序对,则它必然是已按逆序排序的数组,因为对于每一个组成逆序对第一个值的下标i,都存在n-i个逆序对,而这是它所能达到的逆序对个数的最大值。总逆序对数量为,等于
。
c. 解答:
以下是插入排序的伪代码:
现在我们修改该代码,让它支持逆序对运算,修改如下:
如上所示,当循环j结束时,可以得到该数组逆序对的个数c 。
下面我们来证明对外层的j循环,其不变式为:当循环开始时,c为数组A[1..j-1]的逆序对个数,且A[1..j-1]还是由由数组排序重组之前的A[1..j-1]的元素构成。
初始化:第一次循环开始前,由于c被初始化为0,而 A[1..j-1]只有一个元素A[1],不存在逆序对,故不变式成立。
保持:如果对循环不变式的某次迭代为真,即c为当前A[1..j-1]的逆序对个数。那么,在执行本次循环时,子循环通过 i 值(初始化为j-1)循环递减,不断的寻找比A[j]更大的元素。直到循环条件失败为止。又因为插入排序已经证明A[1..j-1]是已排序的数组,所以一旦子循环终止,可以推断A[1..i]由小于或等于A[j]的元素组成,而A[i+1..j-1]由大于A[j]的元素组成,故A[i+1..j-1]的元素个数即为数组A[1..j]中,以A[j]为逆序对第二个元素的逆序对个数。由于在子循环开始之前,c已经为A[1..j-1]的逆序对个数,对于A[1..j]而言,只要将以A[j]为逆序对第二个元素的逆序对个数添加到c中,即可得到A[1..j]的逆序对个数。而插入排序的整个过程仅仅是重排数组索引,所以循环结束时,A[1..j]依然由循环前的元素组成。故不等式成立。
终止:当循环终止时,有j = A.length+1,根据循环不变式的“保持”部分的证明,可得c为A[1..A.length]的逆序对个数组成。
d 解答:
通过修改归并排序,我们可以用θ(nlgn)时间计算逆序对。代码如下: