在lrj的那本<算法竞赛入门经典>第一章中,有一例题,讲的是三整数排序,代码如下:
#include<stdio.h> int main() { int a,b,c,t; scanf("%d%d%d",&a,&b,&c); if(a > b) { t = a; a = b; b = t; } if(a > c) { t = a; a = c; c = t; } if(b > c) { t = b; b = c; c = t; } printf("%d %d %d/n",a,b,c); return 0; }
先(a,b)再(a,c)最后(b,c).是能够将三整数正确排序的.因为经过(a,b) (a,c)后,
元素a就已经是三个整数里面最小的元素了.接下来就比较b和c的大小就行了.
书上给出一个思考题: 其他的检查顺序是否也可以? 如(a,b) (b,c) (a,c).
实验后发现这是不正确的的.
反例: 36 54 7
排序后: 36 7 54
分析:经过(a,b)后,较小的一个是a,较大的一个是b.
经过(b,c)后,较小的一个是b,较大的一个是c.
注意这一步骤中包含了两种情况:
i)b比c小,这时b,c顺序不需要改变,又由步骤1,可知a一定比b小.这时就已经
是有序的了,即a<b<c.
ii)b比c大,这时b,c的值需要交换,交换后b是原来c的值,c是原来b的值,由步骤1
可知原来的b是一定比a大的.于是这时候再进行(a,c)是完全重复步骤1的.也
就是说c此时是一定比a大.但是这时候的b,即原来的c,与a的大小是无法判别
的.
实例: 设a = 36, b = 54, c = 7.
经过(a,b)后,仍然是a = 36,b = 54.
经过(b,c)后,变化为b = 7, c = 54.
此时,再进行(a,c),就相当于又是比较36与54.是没有意义的.
即实际上整个过程有效的比较只有2次,显然这是不够的.不是正确的排序.
改进: 正确的排序顺序可以是:(a,b),(b,c),(a,b).
实质: 若不进行类似上面的分情况讨论,其实也是得出思路:
(a,b)后a是小者,b是大者.
(b,c)后b是小者,c是大者.并且c一定是最大者.
既然确定了c一定是最大者,那么很显然再一次比较(a,b)就可以得到正确的序列.
按这种思路来看书上提供的答案的排序顺序:
(a,b)后a是小者,b是大者.
(a,c)后a是小者,并且a一定是最小者.
同样,很显然再一次比较(b,c)就可以得到正确的序列.
因此,类似于(a,b),(b,c)...(n-1,n)之后,传递的是最大值,即经过这样一系列的
操作后,能确定的是最大值一定是变量n.但是无法确认任何一个较小值.
(可以试试序列34,54,7,2,1)