本节主要介绍一些败者树及其使用场景,然后给出一个败者树的实现。
1. 败者树介绍
败者树
是树形选择排序的一种变形, 主要会用于外部多路归并排序。在大部分情况下我们接触到的都是胜者树
, 即每个非终端节点均表示其左、右孩子节点中的胜者
。反之, 如果在双亲节点中记下刚进行完的这场比赛中的败者,而让胜者去参加更高一层的比赛,便可得到一棵败者树
。
如下图(a)
所示为一棵5-路归并的败者树ls[0..4]
,图中方形节点表示叶子节点(也可看成是外节点),分别为5个归并段中当前参加归并选择的记录的关键字; 败者树中根节点ls[1]
的双亲节点ls[0]为“冠军”,在此指示各归并段中的最小关键字记录为第三段中的当前记录; 节点ls[3]指示 b1 和 b2 两个叶子节点中的败者即b2,而胜者b1和b3(b3是叶子节点b3、b4和b0经过两场比赛后选出来的获胜者)进行比较, 节点ls[1]则指示它们中的败者为b1。在选得最小关键字的记录之后,只要修改叶子节点b3中的值,使其为同一归并段中的下一个记录的关键字,然后从该节点向上和双亲节点所指的关键字进行比较,败者留在该双亲节点,胜者继续向上直至树根的双亲。
上图(b)
所示,当第3个归并段中第2个记录参加归并时,选得的最小关键字记录为第一个归并段中的记录。为了防止在归并过程中某个归并段变空,可以在每个归并段中附加一个关键字为最大值的记录。当选出“冠军”记录的关键字为最大值时,表明此次归并已经完成。由于实现k-路归并的败者树的深度为⌈log2^n⌉ + 1
,则在k个记录中选择最小关键字仅需进行⌈log2^n⌉
次比较。败者树的初始化也容易实现,只要先令所有的非终端节点指向一个含最小关键字的叶子节点,然后从各个叶子节点出发调整非终端节点为新的败者即可。
下面的算法11.1简单描述利用败者树进行k-路归并的过程。为了突出如何利用败者树进行归并,在算法中避开了外存信息存取的细节,可以认为归并段已在内存。算法11.2描述在从败者树选得最小关键字的记录之后,如何从叶到根调整败者树选得下一个最小关键字。算法11.3为初建败者树的过程的算法描述:
最后要提及一点,k值的选择并非越大越好,如何选择合适的k是一个需要综合考虑的问题。
2. 败者树实现
编译运行:
[参看]: