找第二大问题的比较次数

 给定长度为n的数组,数组元素各不相同,如何找出第二大的元素?

1. 顺序比较,先用n-1次比较找出最大值;再用n-2次比较找出剩余数字中的最大值。一共比较2n-3次

2. 败者树:将元素两两分为一组,(奇数则有可能轮空,让这个元素直接进入下一轮);每组决出胜者,又两两分为一组。如此等等,胜者进入下一轮,将元素比较关系组织成树的形式。树叶是所有原始元素,内结点表示一次比较,记录了比赛的败者。另外在根节点之上加一个结点,记录全局的胜者。

注意到,第二名一定被第一名淘汰过,因为如果它被其他人(最好是第三名)淘汰过,那显然自身就不会是第二名。所以,建立败者树之后,只需要遍历从第一名叶子到根的路径上的全部结点,也就是第一名曾经淘汰过的所有结点,其中最大的就是第二名。这样做的比较次数是 

n -1 + \left \lfloor \log n \right \rfloor - 1 = n + \left \lfloor \log n \right \rfloor -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]

对于给定比较序列,我们说明,按照上面的规则,最大元素淘汰的元素数量至少是 \left \lfloor \log n \right \rfloor

由于规则使得权大的才有资格继续获胜,最终的胜者记为x(尽管x的值可能被改变过),x每次获胜,他的权值增加不超过一倍。而最终权值是n,初始权值是1;所以x至少淘汰了 \left \lfloor \log n \right \rfloor 个其他元素。

另一方面,为找找到第二大元素,显然必须要找出最大元素。在剩余元素中找第二名,显然不会比在 “被第一名淘汰的元素集” 里找第二名更好。而我们说明了这个集合至少有 \left \lfloor \log n \right \rfloor 元素。在规模为n的集合里找最大值,至少需要n-1次比较。

因此总的比较次数至少是 n + \left \lfloor \log n \right \rfloor - 2 。也就是败者树算法是最优的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值