用跳表实现游戏排行榜

排行榜有很多种设计方案:

比如数组,排序树,Redis的sort set等,还有这里说的跳表。

先科普一下跳表以及分析一下跳表优劣:

跳表:在普通链表中,给一些节点增加额外的指针,使得这些节点能够一次跨越更多的中间节点,提高了效率。

优点:相比普通链表,由于跳跃的特性,可以节省便利次数,时间复杂度上是O(logN)。相比平衡二叉树,在插入和删除操作上,不需要再进行树的平衡等操作。

缺点:有额外指针的空间消耗,在数据量超大的情况下,性能还是会下降。

跳表结构如下:

每个框框表示一个Node,里面存了一个指针数组,位数越大,跨度越大

先说一下查找

由于位数越大,跨度越大,所以我们从位数高到位数低遍历,对每一位:While循环对比指针指向的Node里的Score是否小于插入成绩,是则指针跳到该Node,否则跳出循环。

uint32 GetRank(int64 InsertCode, uint64 InsertScore)
{
  if(!insertCode)
    return;
  skiplistNode* pNode = header;
  uint32 rank = 0;
  for(int i = CurLevel - 1; i >= 0; i--)
  {
    skiplistNode* pNexNode = pNode->level[i].forward;
    while(pNexNode && pNexNode->Value<=InsertValue)
    {
      rank +=pNode->level[i].span;
      pNode = pNode->level[i].forward;
    }
    if(pNode->Code == InsertCode)
      return rank;
  }
  return 0;
}

能够看懂查找的话,插入和删除也就差不多了,理一下插入流程

1,整体流程是:找到插入位置,记录指针数组,随机一个插入Node的Level,然后插入(与一般的链表插入差不多)

2,建立指针数组和排名数组,比如:skiplistNode* update[MaxLevel]; uint32 rank[MaxLevel];

3,查找。这儿查找需要记录最接近插入位置的每一个跨度的指针,保存到update里

4,随机一下插入Node的Level,通常建议:level = 1; while(1/4概率) ++level; 这样跨度等级不会爆炸

5,如果InsertLevel超过了当前排行榜的Level,就把update数组中超出部分的指针指向header,且每位保存的步长都设置为排行榜的长度Length

6,接着就是常规链表的插入了,这儿相当于同时插入了InsertLevel个链表,根据update数组中的指针,和指针指向的下一个Node,中间插入

7,插入之后,遍历update数组,将里面的InsertLevel位到排行榜CurLevel位的步长加1

8,因为这种设计的排行榜最后面的是最大值,我们在每个Node里存一个后向指针,组成双向链表,方便遍历,所以再简单处理一下Node里的backward指针就可以了

删除的操作和插入差不多,从这一系列的步骤来看,对比平衡树在插入和删除之后,还要调整位置,跳表就不需要这么麻烦了,所以这是一个很好的可以用来设计排行榜的方式。

对了,如果需要在排行榜上保存玩家数据怎么弄?保存在Node里?

不建议直接存Node里,不然其他地方需要数据的时候还需要查找一次,建议Node里存一个唯一标志的Code和成绩就可以了,玩家详细信息用一个HashMap保存在其他地方(如果有多个排行榜的话,还可以共用数据)

原文地址:https://www.cnblogs.com/YoungBig/p/9959107.html

时间: 2024-08-09 11:07:58

用跳表实现游戏排行榜的相关文章

游戏排行榜的一种实现

游戏排行榜的一种实现 我是游戏公司小猿一只,做游戏服务器开发.最近公司准备招新的 C++ 程序员,我一般都会问到一个常见的功能,一万人排行榜怎么实现. 得到的答案很多,比如 “1.直接从数据库中读取:2.用 hash 表:3.用一个环形数组:4.这个问题我要仔细考虑一下.” 对于这些答案我是不满意的,因为从事游戏逻辑开发的人,排行榜功能是一个基础功能,出于“优秀”C++ 程序员的本能,应该细想怎样实现之.对这个功能如果仅仅停留在表面,心理大概推测用什么数据结构,在关键性问题上支支吾吾,我可能不大

SQL SERVER 2014--内存表实现秒杀场景

===================================== 网上针对“秒杀”的解决方案很多,数据拆分化解热点,READPASH解决锁问题,应用程序排队限制并发等等很多方式,各有优缺点,只为证明一句名言:条条大路通罗马. ===================================== 今天拿SQL SERVER 2014的内存表来试水“秒杀”,内存表使用“版本”解决了高并发下锁请求和阻塞的问题,使用HASH索引来处理数据页“热点”的问题,解决了PAGE_LATCH等待,

DropDownList绑定数据表实现两级联动示例

这篇文章主要介绍了DropDownList绑定数据表实现两级联动具体实现,需要的朋友可以参考下 场景一:平时我们在DropDownList控件下添加下拉选项时,都会使用它的Item.Add方法,直接在代码下添加.如果我们想添加或修改下拉选项,则必须去修改源代码.如果几个DropDownList控件的下拉选项相同,我们则需要重复添加好多次,后期的维护工作很不方便. 场景二:我们在12306网站买票时,肯定遇到过这么一种情景:我们需要先选定目的地的省份,选完省份后在城市选框中会自动加载该省份的城市,

用顺序表实现一个循环队列

队列是一种先进先出的线性表,简称FIFO.允许插入的一端为队尾,允许出列的一端为队头. 比如一个队列q=(p1,p2,p3,p4...pn),p1就是那个队头,pn就是队尾.出列时总是从p1开始 向后,入列时总是从pn后面插入.就像敲键盘,依次敲qwr,屏幕上显示的就是qwr,先敲的先显 示. 以下代码是用顺序表实现一个循环队列 1 /** 2 * @filename queue.c 3 * @author haohaibo 4 * @data 2017/4/12 5 * @brief 用顺序表

数据结构之哈希表实现浅析

看了下JAVA里面有HashMap.Hashtable.HashSet三种hash集合的实现源码,这里总结下,理解错误的地方还望指正 HashMap和Hashtable的区别 HashSet和HashMap.Hashtable的区别 HashMap和Hashtable的实现原理 HashMap的简化实现MyHashMap HashMap和Hashtable的区别 两者最主要的区别在于Hashtable是线程安全,而HashMap则非线程安全Hashtable的实现方法里面都添加了synchron

DS之顺序表实现乱序输入顺序输出

顺序表的实例有很多,在学其他的编程语言时,肯定都学过要求输入一串乱序的数字,要求进行排序,实现升序或降序输出.今天就来用顺序表实现乱序输入,顺序输出(升序). 实现上述的功能需要用到的顺序表的基本操作有0基本操作前的准备,1初始化顺序表,6向顺序表插入数据元素. 自己只需写一个排序的函数,排序函数的代码为: <span style="font-size:18px;">//排序函数 void paixu(SqList &L) { for(int i=0;i<L.

hibernate之关于使用连接表实现多对一关联映射

[Hibernate]之关于使用连接表实现多对一关联映射 在我们项目使用中采用中间表最多的一般就是多对一,或者是多对多,当然一对一使用中间表也是可以的,但是这种几率通常少之又少!所以这里重点介绍多对一和一对多的采用中间表进行关联映射! 依然采用Group和Person来描述这个逻辑! Annotations配置 @Entity @Table(name="t_group") publicclass Group {     private Integer id;     private S

hibernate之关于使用连接表实现一对多关联映射

[Hibernate]之关于使用连接表实现一对多关联映射 基于中间表实现的一对多的关联映射,还是比较常见的. Person(人)和Group(组) Annotations配置 @Entity @Table(name="t_group") publicclass Group {     private Integer id;     private String name;     private Set<Person> persons=newHashSet<Perso

放松一下】北美小游戏排行榜TOP10——“点击英雄”

大家平时工作都比较忙,在这里推荐一款游戏让大家放松一下,让你体验到指数爆炸般挣钱的快感. 北美小游戏排行榜TOP10--"点击英雄" 简要说明: 游戏可以挂机,关闭电脑,游戏也会自动赚钱(游戏金币) 操作简单,鼠标点击怪物,升级即可 全球点到爆根本停不下来! 游戏地址:http://bbs.phpthinking.com/forum.php?mod=viewthread&tid=135 游戏截图: 快来一起玩到爆吧!