给定长度为n的数组,数组元素各不相同,如何找出第二大的元素?
1. 顺序比较,先用n-1次比较找出最大值;再用n-2次比较找出剩余数字中的最大值。一共比较2n-3次
2. 败者树:将元素两两分为一组,(奇数则有可能轮空,让这个元素直接进入下一轮);每组决出胜者,又两两分为一组。如此等等,胜者进入下一轮,将元素比较关系组织成树的形式。树叶是所有原始元素,内结点表示一次比较,记录了比赛的败者。另外在根节点之上加一个结点,记录全局的胜者。
注意到,第二名一定被第一名淘汰过,因为如果它被其他人(最好是第三名)淘汰过,那显然自身就不会是第二名。所以,建立败者树之后,只需要遍历从第一名叶子到根的路径上的全部结点,也就是第一名曾经淘汰过的所有结点,其中最大的就是第二名。这样做的比较次数是
这个值实际上也是这个问题的下界。给定输入规模与算法,算法将产生唯一的比较序列,我们针对它构造一个输入,使得算法至少应该比较这么多次。
引入权的概念。算法运行过程中,一个元素x,如果x没有失败过,x的权为目前已知小于等于x的元素的数量;否则x的权为0。这样数组各个元素的权和不变。假设元素是不相同的,初始各个元素的权均为1,即自身。
用类似并查集的方式,用树结构(这里不是败者树)来组织元素。如果x<y,则把x为根的子树挂到y的下面。如何决定x,y的大小关系呢?每次比较,我们都让当前权值较大的元素获胜。权相等则任意。特别地,两个零权元素的比较,我们不改变元素的值。
举例来说,对于n=5,算法给出比较次序(按数组下标): 0, 1; 0, 2; 4, 3; 0, 4
- 01比较权值1=1,让0>1,赋值成[20, 10, ?, ?, ?], 权值[2, 0, 1, 1, 1]
- 02比较权值2=1, 让0>2,赋值成[20, 10, 15, ?, ?],权值[3, 0, 0, 1, 1]
- 43比较权值1=1,让4>3,赋值成[20, 10, 15, 30, 40], 权值[3, 0, 0, 0, 2]
- 04比较权值3>2,让0>4,调整成[50, 10, 15, 30, 40], 权值[5, 0, 0, 0, 0]
对于给定比较序列,我们说明,按照上面的规则,最大元素淘汰的元素数量至少是
由于规则使得权大的才有资格继续获胜,最终的胜者记为x(尽管x的值可能被改变过),x每次获胜,他的权值增加不超过一倍。而最终权值是n,初始权值是1;所以x至少淘汰了 个其他元素。
另一方面,为找找到第二大元素,显然必须要找出最大元素。在剩余元素中找第二名,显然不会比在 “被第一名淘汰的元素集” 里找第二名更好。而我们说明了这个集合至少有 元素。在规模为n的集合里找最大值,至少需要n-1次比较。
因此总的比较次数至少是 。也就是败者树算法是最优的。