排行榜的高效数据结构,即记录列表(名称、点数) - 高效搜索(名称)、搜索(排名)和更新(点数)

Posted

技术标签:

【中文标题】排行榜的高效数据结构,即记录列表(名称、点数) - 高效搜索(名称)、搜索(排名)和更新(点数)【英文标题】:Efficient data structure for a leaderboard, i.e., a list of records (name, points) - Efficient Search(name), Search(rank) and Update(points) 【发布时间】:2014-10-02 23:58:02 【问题描述】:

请建议一种数据结构来表示memory 中的记录列表。每条记录由以下部分组成:

用户名 积分 排名(基于积分) - 可选字段 - 可以存储在记录中,也可以动态计算

数据结构应该有效地支持以下操作的实现:

    插入(记录) - 可能会更改现有记录的排名 删除(记录) - 可能会更改现有记录的排名 GetRecord(name) - 可能是哈希表。 获取记录(排名) 更新(点数) - 可能会改变现有记录的排名

我的主要问题是 GetRecord(rank) 的高效实现,因为排名会经常变化。

我想内存中的DBMS 会是一个很好的解决方案,但请不要建议;请推荐一个数据结构。

【问题讨论】:

【参考方案1】:

基本上,您只需要一对平衡的搜索树,这将允许 O(lg n) 的插入、删除和 getRecord 操作。诀窍是,您将存储指向一组记录对象的指针,而不是将实际数据存储在树中,其中每个记录对象将包含 5 个字段:

    用户名 分值 排名 指向名称树中引用该对象的节点 指向点树中引用该对象的节点。

只有在添加新记录和删除记​​录时才会修改名称树。点树被修改以用于插入和删除,但也用于更新,在找到适当的记录时,删除其点树指针,更新其点计数,然后将新指针添加到点树。

正如您所提到的,如果您愿意,可以使用哈希表代替名称树。这里的关键是您只需将单独的排序索引维护到一组其他无序的记录中,这些记录本身包含指向其索引节点的指针。


点树将是order statistic tree 的一些变体,它不是特定的数据结构,而是二叉搜索树的总称,其操作被修改以保持不变量,从而使请求的排名相关操作比走树更有效率。如何维护不变量的细节取决于所使用的底层平衡搜索树(红黑树、avl 树等)。

【讨论】:

使用名称树查找记录 (O(lg n));使用对象中存储的指针在点树中找到对应的节点;删除该节点(O(lg n));更新总积分;在点树中插入一个新节点(O(lg n));并更新对象记录中的指针。 但这并不关心更新记录或其他记录的排名,对吧?排名基于点数。 一个节点在点树中的排名基本上是该节点右子树中的节点数(加1)。您可以在平衡二叉搜索树上的插入和删除操作期间保持此计数。 那么你的意思是,点数最多(rank=1)的记录将是点树中唯一的叶节点,而点数(rank=n)最小的记录将是没有左孩子的根节点?实际上,点树是右斜树,对吧?这是一种有效的实施方式吗? 不,你需要一个平衡树来实现高效,但是在 any 搜索树中,一个项目大于其左子树中的每个元素并且小于它是正确的每次都在其右子树中。秩 1 将是最右边的节点(从根开始的右子节点之后),秩 n 将是最左边的节点。根是(大约)中间元素。【参考方案2】:

skiplist + hashmap 应该可以工作。

这是 Go 中的一个实现:https://github.com/wangjia184/sortedset

集合中的每个节点都与这些属性相关联。

key 是节点的唯一标识,在您的情况下为“用户名”。 value 是与节点关联的任何值 score 一个数字决定了集合中的顺序(排名),在你的情况下是“点”

集合中的每个节点都与一个键相关联。虽然钥匙是独一无二的, 分数可能会重复。节点按顺序排列(从低分到 高分)而不是事后订购。如果分数相同,则 节点按字典顺序按其键排序。中的每个节点 set 也可以通过 rank 来访问,rank 表示在 排序集。

排序集的一个典型用例是大型在线排行榜中的排行榜 游戏,每次提交新分数时,您都会使用 AddOrUpdate() 方法。您可以轻松地使用***用户 GetByRankRange() 方法,你也可以,给定一个用户名,返回它的 使用 FindRank() 方法在列表中排名。使用 FindRank() 和 GetByRankRange() 一起可以向用户显示分数类似于 给定用户。一切都很快。

【讨论】:

什么是键,什么是值?您可以在答案中包含更多您的算法吗? 顺便提一下,这正是redis排序列表的实现方式。 redis.io/topics/data-types-intro @zuselegacy 是的,但是这个问题需要一个进程中的问题【参考方案3】:

寻找包含按顺序记录编号选择记录的功能的 DBMS。

见:How to select the nth row in a SQL database table?

构造一个包含 UserName 列和 Points 列的表。使 UserName 成为主索引。在 Points 上构造一个二级非唯一维护索引。

要获取排名为 R 的记录,请选择 Points 上的索引并移动到记录 R。

这使得 DBMS 引擎可以完成大部分工作并使您的部分保持简单。

【讨论】:

感谢您的回答,但我正在寻找一种数据结构的方法

以上是关于排行榜的高效数据结构,即记录列表(名称、点数) - 高效搜索(名称)、搜索(排名)和更新(点数)的主要内容,如果未能解决你的问题,请参考以下文章

如何将 map.getBounds() 作为 4 个浮点数的列表?

高效实时数据排行榜实现

(3)Redis zset原理

网易云音乐Java版爬虫

高效更新大量核心数据记录

使用Redis构建高效稳定低延迟的排行榜业务