锦标赛排序
利用二叉树这个数据结构,或者说工具,我们就能实现一个经典的计算机算法,叫做锦标赛排序算法。 顾名思义,它是受到体育比赛的启发想出来的。
在单淘汰的锦标赛中,选手们两两比赛,胜者晋级,败者被淘汰。比如世界乒乓球锦标赛或者大满贯网球赛就是这么进行的。
这样一来,就可以把比赛的赛程和结果对应成一个二叉树。在树中每一个选手是二叉树中的一个叶子结点,每一场比赛就相当于两个数字在比大小。数字大的选手获胜进入下一轮,也就是说比大小,大的那个选手,进入上一层,成为枝干上的根。
所以,进入到某一轮比赛的选手,其实都是某个子树干的根结点。最后的冠军自然就是整个二叉树的根结点。当然,这种赛制的合理性来自下面一个假设:如果张三赢了李四,李四赢了王五,那么张三一定能赢王五。 也就是说:A>B, B>C, 那么必然有A>C。我们不妨称这种合理的假设为“输赢的传递性”。
只要上面这种胜负的传递性成立,通过这种比赛的结果得到的冠军,一定是最好的选手。但是,第二名是否如此,就难说了。因为冠军一路打下来,被他刷掉的选手可能水平都不差,只是运气不好,提前遇到他了,在决赛之前被淘汰了。
比如说在某次网球比赛中,德约科维奇(人称小德)半决赛赢了费德勒,决赛赢了纳达尔。小德的冠军,不会有什么异议,但你说到底是纳达尔该得亚军,还是费德勒更厉害,还真不好说。费德勒只能怪自己那次抽签运气不好。因此,如果真要较真,就需要把被冠军淘汰下来的人放到一个组里再相互比赛,才能知道谁是亚军。当然,如今体育比赛规则已经成型,大家遵守就好,不必那么麻烦赛出第二名。
但是,在工程中如果要对比两个数字的大小,总不能说哪个数字最后被最大的比下去,就是第二大的吧。因此,如果采用类似锦标赛的方法排出了一、二、三名来,第一大的数字可以完全按照锦标赛淘汰制的方式来。但是第二大的数字,就需要从所有与最大数字比较过被淘汰的数字中,再次比较选择才能确定。当第二大的数字确定后,就可以用这种方法找到第三大的数字了。
这种算法,由于受到锦标赛的启发,因此被称为是“锦标赛排序法”(也称为树形选择排序)。
总结一下这种方法,它分为两步:
- 把所有的数字放到二叉树的叶子结点,然后按照锦标赛单淘汰的方式,两两比较选出最大的。
- 对于第二大的,从所有被最大的数字淘汰的数字中选择。
比如在某次比赛中,被小德淘汰的分别是纳达尔、费德勒、穆雷等人,那么这些人再进行单淘汰,选亚军。对于第三、第四大的数字,可以以此类推。
如果用这种方式将所有的数字排序,算法的复杂度,或者说量级是 N 乘以 Log N,和快速排序差不多。
那么为什么不直接使用快速排序,而要发明出这样一种不太容易理解的算法呢?因为在特定的场合下,它更快速。比如说,如果我们只需要选出第一名,这种算法的复杂度只有 N,不是 N 乘以 Log N。如果还需要选出第二名,则额外增加Log N 次计算就可以了,对第三名也是如此。也就是说,这种方法在从N个选手中选出K个选手的事情中特别快。
示例
假定有二十五名短跑选手比赛竞争金银铜牌,赛场上有五条赛道,因此一次可以有五个人同时比赛。比赛并不计时,只看相应的名次。假如选手的发挥是稳定的,也就是说如果约翰比张三跑得快,张三比凯利跑得快,那么约翰一定比凯利跑得快。最少需要几组比赛才能决出前三名?
大家经过简单的思考,通常会认为需要 8 次才能决出前 3 名,分别是:
- 将25名选手分为五个组,每组五个人,为了便于说明,把这25人根据所在的组进行编号,A1-A5 在A组,B1-B5 在B组……最后 E1-E5 在最后的E组。然后让每个组分别比赛,排出各组的名次来。我们假设他们的名次就是他们在小组中的编号,即 A 组的名次是 A1、A2、A3、A4、A5…
- 让各组的第一名,也就是 A1、B1、C1、D1、E1 再比一次。这样就能决出第一名。由于 A1 是第一名,根据我们前面讲的淘汰赛的问题,A2 可能也很厉害,只是运气不好,小组赛遇到了 A1,当 A1 已经获得冠军了,他就应该作为亚军的候选。接下来,就进入第三步。
- A2 和另外四个组的第一名竞争亚军。如果这一次 A 2赢了,他显然是亚军,就由 A3 递进参加争夺第三名的比赛。如果A2没有赢,另四个组的某个第一名赢了,那个赢的人是亚军,就由那个组下一位选手递进,决逐第三名。
如果这样来规划,实际上还会有许多不必要的比较。前两轮比赛肯定是必须的,它要决出各组的名次和整体的第一名。
但是,在第六组比赛(即五个第一名的比赛)结束之后,最后的两名已经没有资格决逐前三名了。我们不妨假设那一次比赛从最快到最慢的结果是 A1、B1、C1、D1、E1。在D1和E1之前已经有三名选手了,他们肯定不是前三名。
那么谁还会是第二名的候选呢?根据锦标赛排序的原则,直接输给第一名的人,也就是 A 组中的 A2,以及最后附加赛输给他的 B1,仅此两人而已。除了 A2 和 B1,谁还会是第三名的候选呢?和 A1 在某一组比赛的第三名,他们是 A3、C1,或者输给第二名候选人 B1的人那个人,即B2。
因此,第二、三名的候选人一共只有五个,即 A2、A3、B1、B2 和 C1。刚好凑一组。让他们五个人再跑一次即可。这样加上前六次,只需要赛七组,这是最佳的方法。