帮我写一个跳表实现得玩家排行榜,需要支持快速查询玩家所在排名。需要支持不同玩家同积分按上榜时间排名
时间: 2024-02-27 09:51:41 浏览: 115
好的,下面是一个跳表实现的玩家排行榜,支持快速查询玩家所在排名,并且支持不同玩家同积分按上榜时间排名。假设我们用一个玩家结构体来表示玩家信息,包含玩家ID、分数和上榜时间:
```c++
struct Player {
int id;
int score;
time_t time;
};
```
首先,我们需要定义跳表节点的结构体:
```c++
struct SkipNode {
Player player;
vector<SkipNode*> next;
SkipNode(const Player& player, int level): player(player) {
next.resize(level);
}
};
```
其中,`next` 是一个指向下一个节点的指针数组,长度为当前节点的层数。
接下来,我们定义一个玩家排行榜类:
```c++
class PlayerRanking {
public:
PlayerRanking(): head(new SkipNode(Player{-1, -1, 0}, MAX_LEVEL)), level(1) {}
~PlayerRanking() {
SkipNode* p = head;
while (p) {
SkipNode* q = p;
p = p->next[0];
delete q;
}
}
void addPlayer(const Player& player) {
SkipNode* p = head;
vector<SkipNode*> update(MAX_LEVEL);
for (int i = level - 1; i >= 0; i--) {
while (p->next[i] && cmpPlayer(p->next[i]->player, player) < 0) {
p = p->next[i];
}
update[i] = p;
}
p = p->next[0];
if (p && cmpPlayer(p->player, player) == 0) {
// 如果已经存在相同分数的玩家,则更新玩家信息
p->player = player;
} else {
// 否则插入新节点
int newLevel = randomLevel();
if (newLevel > level) {
for (int i = level; i < newLevel; i++) {
update[i] = head;
}
level = newLevel;
}
p = new SkipNode(player, newLevel);
for (int i = 0; i < newLevel; i++) {
p->next[i] = update[i]->next[i];
update[i]->next[i] = p;
}
}
}
int getPlayerRank(int playerId) {
SkipNode* p = head;
for (int i = level - 1; i >= 0; i--) {
while (p->next[i] && p->next[i]->player.id <= playerId) {
if (p->next[i]->player.id == playerId) {
return getPlayerRank(p->next[i]);
}
p = p->next[i];
}
}
return -1; // 找不到对应的玩家
}
private:
static const int MAX_LEVEL = 16;
SkipNode* head;
int level;
int getPlayerRank(SkipNode* node) {
int rank = 0;
SkipNode* p = head;
for (int i = level - 1; i >= 0; i--) {
while (p->next[i] && cmpPlayer(p->next[i]->player, node->player) < 0) {
rank += countNodes(p->next[i]);
p = p->next[i];
}
}
return rank + countNodes(node->next[0]) + 1;
}
int countNodes(SkipNode* node) {
int cnt = 0;
while (node) {
cnt++;
node = node->next[0];
}
return cnt;
}
int cmpPlayer(const Player& p1, const Player& p2) {
if (p1.score != p2.score) {
return p1.score > p2.score ? 1 : -1;
} else if (p1.time != p2.time) {
return p1.time < p2.time ? 1 : -1;
} else {
return p1.id - p2.id;
}
}
int randomLevel() {
int level = 1;
while (rand() % 2 == 0 && level < MAX_LEVEL) {
level++;
}
return level;
}
};
```
在上面的实现中,我们使用了随机化的方法来决定每个节点的层数,以保证跳表高度的期望是 O(log n)。`addPlayer` 方法用于向排行榜中添加新玩家,如果已经存在相同分数和时间的玩家,则更新玩家信息。`getPlayerRank` 方法用于查询玩家所在排名。
在 `getPlayerRank` 方法中,我们先从跳表的最高层开始,一层一层向下查找,直到找到对应玩家的节点或者找到比该玩家分数低的节点。如果找到了对应玩家的节点,则调用 `getPlayerRank(SkipNode* node)` 方法来计算该玩家的排名。
在 `getPlayerRank(SkipNode* node)` 方法中,我们从跳表的顶层开始,一层一层向下查找,直到找到对应节点,中间经过的所有节点的数量就是该节点的排名。注意,这里的节点数量不包括该节点本身,因此最后要加上 1。
在 `cmpPlayer` 方法中,我们定义了一个比较函数,用于比较两个玩家的大小。首先比较分数,分数相同时比较上榜时间,时间相同时比较玩家ID。
完整代码如下:
阅读全文