全服排行榜算法思路

排行榜是游戏中一个常见的系统,不过要做好并不容易,比如要对全服几W名玩家做实时排行的话,性能需要花心思去优化的问题。

这里设计了一个基于桶排序的面向全服玩家的通用排行榜

一个简单的思路就是按排序的KEY的分值进行分桶,但是一个弊端就是随着游戏生命推进,会出现一个分数段类的玩家大规模集中,导致分桶失去意义。

所以这里采用了对桶的容量做上限,然后达到上限就分裂桶然后动态计算桶的上下限的做法

桶的定义如下(代码采用c#编写):

        class sub_rank_list
        {
            public Hashtable low;
            public Hashtable up;

            private HashSet<String> mem_set;
            public List<Hashtable> rank_list;

            private IComparer<Hashtable> comparer;
        }

使用Hashtable描述排序的信息是为了排行榜的通用性。

每个Hashtable中需要包含对应玩家的unionid和排序信息

玩家的unionid保存在mem_set中(在mem_set中可以找到玩家的id,表示玩家在这个桶内)

要获取一个玩家在排行榜上的排名,则先查找到玩家在哪个桶内,然后计算 这个桶前有多少玩家和玩家在该桶内的排名之和就是玩家的排名

        public int get_rank(String uuid)
        {
            int rank = 0;
            foreach(var sub_rank_list in rank_data)
            {
                if (!sub_rank_list.in_rank(uuid))
                {
                    rank += sub_rank_list.count();
                    continue;
                }

                rank += sub_rank_list.rank(uuid);

                break;
            }

            return rank;
        }

整个排行榜的代码如下:

using System;
using System.Collections;
using System.Collections.Generic;

namespace rank
{
    //升序排行
    class rank
    {
        public rank(IComparer<Hashtable> _comparer)
        {
            rank_data = new List<sub_rank_list>();
            comparer = _comparer;
        }

        public void clear_rank()
        {
            rank_data = new List<sub_rank_list>();
        }

        public void update_rank(Hashtable data)
        {
            String uuid = (String)data["uuid"];

            foreach(var sub_list in rank_data)
            {
                if (!sub_list.in_rank(uuid))
                {
                    continue;
                }

                if (sub_list.in_range(data))
                {
                    sub_list.update_rank(data);
                }
                else
                {
                    sub_list.del_rank(uuid);
                    insert_rank(data);
                }

                break;
            }
        }

        public void insert_rank(Hashtable data)
        {
            sub_rank_list sub_list = null;
            sub_rank_list up_list = null;
            sub_rank_list low_list = null;
            foreach (var _sub_list in rank_data)
            {
                if (up_list == null)
                {
                    up_list = _sub_list;
                }
                else
                {
                    if (comparer.Compare(up_list.up, _sub_list.up) < 0)
                    {
                        up_list = _sub_list;
                    }
                }

                if (low_list == null)
                {
                    low_list = _sub_list;
                }
                else
                {
                    if (comparer.Compare(low_list.low, _sub_list.low) > 0)
                    {
                        low_list = _sub_list;
                    }
                }

                if (!_sub_list.in_range(data))
                {
                    continue;
                }

                sub_list = _sub_list;
                break;
            }
            if (sub_list == null)
            {
                if (low_list == null && up_list == null)
                {
                    sub_list = new sub_rank_list(data, data, comparer);
                }
                else
                {
                    if (comparer.Compare(low_list.low, data) > 0)
                    {
                        sub_list = low_list;
                        sub_list.low = data;
                    }
                    if (comparer.Compare(up_list.up, data) < 0)
                    {
                        sub_list = up_list;
                        sub_list.up = data;
                    }
                }
            }
            sub_list.insert_rank(data);

            if (sub_list.count() > 2000)
            {
                var new_rank = sub_list.split_rank();
                rank_data.Add(new_rank);

                for(int i = 0; i < rank_data.Count - 1; i++)
                {
                    var rank1 = rank_data[i];
                    var rank2 = rank_data[i + 1];

                    if (comparer.Compare(rank1.low, rank2.up) < 0)
                    {
                        rank_data[i] = rank2;
                        rank_data[i + 1] = rank1;
                    }
                }
            }
        }

        public int get_rank(String uuid)
        {
            int rank = 0;
            foreach(var sub_rank_list in rank_data)
            {
                if (!sub_rank_list.in_rank(uuid))
                {
                    rank += sub_rank_list.count();
                    continue;
                }

                rank += sub_rank_list.rank(uuid);

                break;
            }

            return rank;
        }

        public Hashtable get_rank(int rank)
        {
            int rank_low = 0;
            int rank_up = 1;
            foreach(var sub_rank_list in rank_data)
            {
                rank_low = rank_up;
                rank_up += sub_rank_list.count();

                if (rank >= rank_low && rank < rank_up)
                {
                    return sub_rank_list.rank_list[rank - rank_low];
                }
            }

            return null;
        }

        class sub_rank_list
        {
            public sub_rank_list(Hashtable _low, Hashtable _up, IComparer<Hashtable> _comparer)
            {
                low = _low;
                up = _up;
                mem_set = new HashSet<String>();
                rank_list = new List<Hashtable>();

                comparer = _comparer;
            }

            public bool in_range(Hashtable data)
            {
                int c1 = comparer.Compare(data, up);
                int c2 = comparer.Compare(data, low);

                return c1 <= 0 && c2 > 0;
            }

            public bool in_rank(String uuid)
            {
                return mem_set.Contains(uuid);
            }

            public int count()
            {
                return mem_set.Count;
            }

            public int rank(String uuid)
            {
                int rank = 0;
                foreach(var mem in rank_list)
                {
                    rank++;

                    if (uuid == (String)mem["uuid"])
                    {
                        break;
                    }
                }

                return rank;
            }

            public void update_rank(Hashtable data)
            {
                foreach(var _data in rank_list)
                {
                    if ((String)_data["uuid"] != (String)data["uuid"])
                    {
                        continue;
                    }

                    rank_list.Remove(_data);
                    break;
                }
                rank_list.Add(data);
                rank_list.Sort(comparer);
            }

            public void del_rank(String uuid)
            {
                foreach (var _data in rank_list)
                {
                    if ((String)_data["uuid"] != uuid)
                    {
                        continue;
                    }

                    mem_set.Remove(uuid);
                    rank_list.Remove(_data);
                    break;
                }
            }

            public void insert_rank(Hashtable data)
            {
                mem_set.Add((String)data["uuid"]);
                rank_list.Add(data);
                rank_list.Sort(comparer);
            }

            public sub_rank_list split_rank()
            {
                sub_rank_list new_rank = new sub_rank_list(low, up, comparer);

                int _count = count() / 2;
                Hashtable data = null;
                for (int i = 0; i < _count; i++)
                {
                    data = rank_list[i];
                    del_rank((String)data["uuid"]);
                    new_rank.insert_rank(data);
                }
                new_rank.low = data;
                up = rank_list[0];

                return new_rank;
            }

            public Hashtable low;
            public Hashtable up;

            private HashSet<String> mem_set;
            public List<Hashtable> rank_list;

            private IComparer<Hashtable> comparer;
        }
        private List<sub_rank_list> rank_data;
        private IComparer<Hashtable> comparer;
    }
}
时间: 2024-10-06 07:43:20

全服排行榜算法思路的相关文章

常见排序算法思路和简单代码实现

算法名称 算法思路 简单代码实现                        

“全服单世界”的终极目标即“虚拟世界”

无意间整理硬盘发现三年前自己写的东西,现在看来还是很震撼,竟然有那么多精力与激情去思考:如下: 今天听了“全服单世界”的讲座,这种设想很震撼,虽然我自己之前也有过这种想法,但是技术出身的我明白这在技术上的难度,也就一笑了之了,今天看到这个讲座真的把它提了上来,那我也说说我的想法,包括我认为“全服单世界”它本身是什么和技术上的问题. 社交网络,自facebook之后这中模式就火开了,按照六度分隔理论,“世界上任何两个人之间的距离不会超过6个人”,人与人之间的关系是很复杂的,facebook类的社交

Modified LCS (扩展欧几里德)细写了对这个算法思路的理解

题目:Modified LCS 为过此题去仔细研究了下扩展欧几里德算法,领悟了一些精华. 模板为: void gcd(ll a, ll b, ll& d, ll& x, ll& y) { if(!b) {d = a; x = 1; y = 0;} else{ gcd(b, a%b, d, y, x); y -= x*(a/b);} } 这里算出来的x,y是对于方程ax+by=gcd(a,b)而言的一组解. 为什么叫扩展欧几里德说明肯定用了欧几里德算法的原理即:gcd(a,b)=gc

全解排序算法

全解排序算法 排序:就是重新排列表中的元素,是表中的元素满足按关键字递增或递减的过程.为了查找方便,通常要求计算机中的表是按关键字有序的.排序的确切定义如下: 输入:n个记录R1,R2, ...,Rn对应的关键字为k1,k2,...,kn. 输出:输入序列的一个重排R1',R2', ...,Rn',使得有k1'<=k2'<=...<=kn'(其中"<="办以换成其他的比较大小的符号). 算法的稳定性:如果待排序表中有两个元素Ri.Rj,其对应的关键字keyi=k

【常用算法思路分析系列】字符串高频题集

本文是[常用算法思路分析系列]的第二篇,分析字符串相关的高频题目.第一篇是关于排序相关的高频题,还没有看的同学请移步:[常用算法思路分析系列]排序高频题集 1.KMP字符匹配 对于两棵彼此独立的二叉树A和B,请编写一个高效算法,检查A中是否存在一棵子树与B树的拓扑结构完全相同,即给定两棵二叉树的头结点A和B,请返回一个boolean值,代表A中是否存在一棵同构于B的子树.上述其实就是一个字符匹配的问题,我们将A.B两棵二叉树进行遍历,得到一个字符串,就是判断B串是否是A串的子串.而字符匹配常用的

【常用算法思路分析系列】与二分搜索相关高频题

本文是[常用算法思路分析系列]的第五篇,总结二分搜索相关的高频题目和解题思路.本文分析如下几个问题:1.求数组局部最小值问题:2.元素最左出现的位置:3.循环有序数组求最小值:4.最左原位:5.完全二叉树计算结点数:6.快速N次方. 本系列前四篇导航: [常用算法思路分析系列]排序高频题集 [常用算法思路分析系列]字符串高频题集 [常用算法思路分析系列]栈和队列高频题集(修改版) [常用算法思路分析系列]链表相关高频题集 二分搜索的重要提醒: 一般我们选择中点进行搜索,会写成mid = (lef

【常用算法思路分析系列】排序高频题集

最近在牛客上整理常用的一些算法思路,[常用算法思路分析系列]主要是针对一些高频算法笔试.面试题目的解题思路进行总结,大部分也给出了具体的代码实现,本篇文章是对排序相关题目的思路分析. 1.简单分类 首先对一些常用算法按空间复杂度进行分类如下: O(1): 冒泡排序.选择排序.插入排序.希尔排序.堆排序 O(logN)~O(N): 快速排序 O(N): 归并排序 O(M): 计数排序.基数排序 2.对一个基本有序的有序的数组排序,选择哪种排序算法? 基本有序:指如果把数组排好序的话,每个元素移动的

Johnson 全源最短路径算法

解决单源最短路径问题(Single Source Shortest Paths Problem)的算法包括: Dijkstra 单源最短路径算法:时间复杂度为 O(E + VlogV),要求权值非负: Bellman-Ford 单源最短路径算法:时间复杂度为 O(VE),适用于带负权值情况: 对于全源最短路径问题(All-Pairs Shortest Paths Problem),可以认为是单源最短路径问题的推广,即分别以每个顶点作为源顶点并求其至其它顶点的最短距离.例如,对每个顶点应用 Bel

Floyd-Warshall 全源最短路径算法

Floyd-Warshall 算法采用动态规划方案来解决在一个有向图 G = (V, E) 上每对顶点间的最短路径问题,即全源最短路径问题(All-Pairs Shortest Paths Problem),其中图 G 允许存在权值为负的边,但不存在权值为负的回路.Floyd-Warshall 算法的运行时间为 Θ(V3). Floyd-Warshall 算法由 Robert Floyd 于 1962 年提出,但其实质上与 Bernad Roy 于 1959 年和 Stephen Warshal