赢者树总 :
C++(数据结构与算法):45---竞赛树/选择树(赢者树、输者树)_董哥的黑板报-CSDN博客
//
// main.cpp
// winner
//
// Created by 🐻先生 on 2021/11/21.
//
#include<iostream>
#include<queue>
using namespace std;
template<class T>
class winnerTree
{
public:
virtual ~winnerTree() {}
//用数组thePlayer[1:numberOfPlayers]生成赢者树
virtual void initialize(T *thePlayer, int theNumberOfPlayers) = 0;
//返回赢者的索引
virtual int winner()const = 0;
//在参赛者thePLayer的分数变化后重赛
virtual void rePlay(int thePLayer) = 0;
};
struct player
{
int id, key;
operator int() const { return key; }
};
template<class T>
class completeWinnerTree :public winnerTree<T>//最小赢者树
{
public:
completeWinnerTree(T *thePlayer, int theNumerOfPlayers) {
//构造函数,将赢者树初始化为空,然后生成赢者树
this->tree = nullptr;
initialize(thePlayer, theNumerOfPlayers);
}
~completeWinnerTree() {
//析构函数
if (tree) {
delete[] tree;
}
tree = nullptr;
}
//用数组thePlayer[1:numberOfPlayers]生成赢者树
void initialize(T *thePlayer, int theNumberOfPlayers);
//返回最终赢者的索引
int winner()const { return this->tree[1]; }
//返回竞赛树某个节点的赢者
int winner(int i) const{
return (i < this->numberOfPlayers) ? this->tree[i] : 0;
}
//在参赛者thePLayer的分数变化后重赛
void rePlay(int thePlayer);
//输出赢者树中的一些信息
void output()const;
void winnerToLoserTree();
int RElowExt(){return lowExt;} //供对象使用
int REoffset(){return offset;}
int* root(){return tree};
int leftChild(int i){ if(i*2<numberOfPlayers-1) return temp*2; return 0;}
int rightChild(int i){ if(i*2+1<numberOfPlayers-1) return temp*2+1; return 0;}
private:
/*
对tree[p]节点进行比赛,leftChild为左子节点,rightChild为右子节点
如果还有父节点,继续向上比赛
*/
void play(int p, int leftChild, int rightChild);
private:
int lowExt; //最底层外部节点个数
int offset; //offset=2*s-1(s为最底层最左端的内部节点)
int *tree; //赢者树
int numberOfPlayers;//竞赛选手的数量
T *player; //保存竞赛选手
};
template<class T>
void completeWinnerTree<T>::winnerToLoserTree(){
queue<int> q;//层次遍历 赢者树tree【i】 的孩子可以知道比赛的选手
int temp;
bool flag=1;
int left,right,rightFirstPlayer;
int s=numberOfPlayers-lowExt/2; //最后一个内部节点序号 反解
q.push(1);
while(!q.empty()){
temp=q.front();
q.pop();
if(temp*2<numberOfPlayers-1){//反解定位到左子树
left= player[ tree[temp*2] ];
if(flag)
tree[0]= tree[temp*2];
}
else if( temp*2>=s )
left= player[2*temp-offset];
else
left= player[2*temp+1+lowExt-numberOfPlayers];
if(temp*2+1<numberOfPlayers-1){
right=player[ tree[temp*2+1] ];
if(flag)
rightFirstPlayer=tree[temp*2+1];
}
else if( temp*2+1>=s )
right= player[2*temp-offset+1];
else
left= player[2*temp+1+lowExt-numberOfPlayers+1];
if(flag){
//赢者是最小的
if(left>right)
tree[0]=rightFirstPlayer;
flag=0;
}
tree[temp]= left>right? left:right;
if(temp*2<numberOfPlayers)
q.push(temp*2);
if(temp*2+1<numberOfPlayers)
q.push(temp*2+1);
}
cout<<tree[0];
}
//用数组thePlayer[1:numberOfPlayers]生成赢者树
template<class T>
void completeWinnerTree<T>::initialize(T *thePlayer, int theNumberOfPlayers)
{
int n = theNumberOfPlayers;//竞赛者的数量 //如果竞赛者的数目小于2,不能进行竞赛
if (n < 2)
{
cout<<"error!"<<endl;
exit(1);
}
//初始化类内数据成员
this->player = thePlayer; //竞赛者
this->numberOfPlayers = n;//当前竞赛者的数目
delete[] this->tree; //删除竞赛树
this->tree = new int[n]; //创建竞赛树数组
//计算s=2^log (n-1)
int i, s;
for (s = 1; 2 * s <= n - 1; s += s);
this->lowExt = 2 * (n - s);//最底层外部节点个数(见公式) 13-1
this->offset = 2 * s - 1;
//为最低级别的外部节点进行匹配
for (i = 2; i <= this->lowExt; i += 2) //从外部右孩子节点来比较
play((this->offset + i) / 2, i - 1, i);
//处理剩余的外部节点
if (n % 2 == 1) {
//特殊情况下奇数n,发挥内部和外部节点
play(n / 2, this->tree[n - 1], this->lowExt + 1);
i = this->lowExt + 3;
}
else {
i = this->lowExt + 2;
}
//i是最左边剩余的外部节点
for (; i <= n; i += 2)
play((i - this->lowExt + n - 1) / 2, i - 1, i);
}
/*
对tree[p]节点进行比赛,leftChild为左子节点,rightChild为右子节点
如果还有父节点,继续向上比赛
*/
template<class T>
void completeWinnerTree<T>::play(int p, int leftChild, int rightChild)
{
//因为为最小赢者树,所以返回值比较小的为赢者
//* 将play 和replay 的<= 改为> 即为最大赢者树