算法导论 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. 给出一个确定在n个元素的任何排列中逆序对数量的算法,最坏情况需要θ(nlgn)时间。(提示:修改归并排序)

解答:

a 解答:

5个逆序对分别为<2, 1>, <3, 1>, <8, 6>, <8, 1>, <6, 1>

b 解答:

如果集合{1, 2, ..., n}有最多的逆序对,则它必然是已按逆序排序的数组,因为对于每一个组成逆序对第一个值的下标i,都存在n-i个逆序对,而这是它所能达到的逆序对个数的最大值。总逆序对数量为\sum_{k=1}^{n-1}k,等于 \frac{n(n-1))}{2} 。

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)时间计算逆序对。代码如下:

 

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 这道题要求我们输出一个整数的各位数字。我们可以将这个整数转换成字符串,然后再输出字符串中的每个字符,即可得到各位数字的排列。如果不允许使用字符串,我们也可以使用循环和取模运算来逐位输出整数的各位数字。 ### 回答2: 题目要求输出一个整数的各位数字,这意味着我们需要先将这个整数拆分为它的各个位,然后按相反的顺输出这些数字。 为了实现这个目标,我们需要考虑如何拆分整数。一种方法是使用取模运算符(%)和整除运算符(//)。通过使用取模运算符,我们可以得到整数除以10的余数,这即表示了整数的最低一位数字。然后我们可以用整除运算符将整数除以10,从而消去最低一位数字并得到新的整数。通过重复这个过程,我们可以得到整数的所有数字,直到它变为0为止。 一旦我们得到了整数的各个位数,就可以按输出它们了。可以使用余数符号来计算最低位数字,并使用print函数将每个数字输出。当然,由于我们是在反向输出数字,所以我们需要将数字存储在列表或字符串中,然后再一次性输出。 下面是一个示例代码,它演示了如何将一个整数反向输出为字符串: ``` num = int(input("请输入一个整数:")) digits = [] while num != 0: digit = num % 10 digits.append(str(digit)) num = num // 10 reverse = ''.join(digits) print("输出的数字为:", reverse) ``` 在这段代码中,我们首先要求用户输入一个整数。然后,我们创建一个空列表digits,我们会将该整数的各位数字依次添加到其中。接下来,我们循环处理该整数,直到整数变为0。在每次循环中,我们使用取模运算符获取该整数的最低位数字,并将其转换为字符串类型,最后将其添加到digits列表中。然后,我们使用整除运算符将该整数除以10,这样我们就可以消去最低一位数字并得到新的整数。我们重复这个循环,直到该整数变为0。 接下来,我们需要将digits列表中的数字反转,并按顺连接它们,形成一个字符串。我们可以使用join方法来实现这一目标。join方法将在列表中的每个项目之间插入一个字符串,并返回所有项目的合并结果。我们将一个空字符串作为参数传递给join方法,这样所有数字就会按顺连接,形成的数字字符串。 最后,我们使用print函数将的数字字符串输出到屏幕上,以便用户查看。 总之,输出一个整数的各位数字是一个很有趣的编程问题,需要使用循环,条件语句,算术操作和字符串连接等多种编程技巧。 ### 回答3: 题目要求我们对于一个给定的整数,将其各位数字输出。那么我们可以先将这个整数按照个位、十位、百位……的顺依次拆分开来,然后再把这些数字输出即可。 以整数12345为例,我们可以用以下方法拆分: 1. 取余数:12345 % 10 = 5,表示个位数字为5; 2. 整除10:12345 / 10 = 1234,表示去掉个位数字后剩下的整数为1234; 3. 继续1、2步,得到十位数字4、百位数字3、千位数字2和万位数字1。 最后,我们将这些数字输出即可得到结果:54321。 当然,如果我们使用循环语句可以更加简便的实现。我们可以将拆分数字的步骤用一个while循环不断进行,每次取余数得到最后一位数字并且将整数除以10,直到整个整数被拆分完。然后再用另一个循环依次输出这些数字即可。 实现的代码如下: ``` #include <stdio.h> int main() { int num, digit; printf("请输入一个整数:"); scanf("%d", &num); // 拆分数字并输出 while (num != 0) { digit = num % 10; num /= 10; printf("%d", digit); } printf("\n"); return 0; } ``` 这样,对于任意输入的整数,程都可以正确地输出其各位数字的

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值