实时更新的排行榜

这是根据实际开发经验总结出来的一个排行榜维护工具。

假设排行榜按积分来排序,核心功能需求:①某一个用户积分改变后,需要立即更新受影响用户的名次;②同分情况下,先来的用户排前面,后来的用户排后面。

实现

用一个支持随机访问的容器(如动态数组)保存用户数据(包括用户ID和用户积分)作为排行榜,下标+1即名次,另外用一个映射容器(如Map、HashMap)存放用户ID到名次的映射,value - 1即数组索引,用于快速获取用户的名次。

情况分三种:

①用户已在排行榜,积分发生了增加或减少;②当新用户进入排行榜。

第一种情况的处理方法:

从映射容器中获取到用户的名次,然后就能获取到前一名次的用户和后一名次的用户数据;如果积分多于前一名次的用户,则与前一名次的用户交换名次,然后继续跟前面的用户比较;如果积分少于后一名次的用户,则与后一名次的用户交换名次,然后继续跟后面的用户比较。

第二种情况的处理方法:

将用户插入排行榜最后面,然后按照第一种情况继续处理。

源码

#ifndef RANKING_LIST_H
#define RANKING_LIST_H

#include <vector>
#include <map>
#include <algorithm>

/*
 * 排行榜维护工具,名次从1开始,0为无效名次
 * @_Tp  必要的数据(如用户ID)
 * @_Score  积分类型
 * @_ScoreCompare  积分比较器
 * @_UniqueCompare  数据唯一性比较器 
 * @_MapContainer  映射容器
 * @_RandomContainer  随机访问容器
 */
template<typename _Tp,
         typename _Score,
         typename _ScoreCompare = std::less<_Score>,
         typename _UniqueCompare = std::less<_Tp>,
         typename _MapContainer = std::map<_Tp, std::size_t, _UniqueCompare>,
         typename _RandomContainer = std::vector<std::pair<_Tp, _Score>
         >
        >
class RankingList
{
public:
    typedef _Score score_type;
    typedef _Tp value_type;
    typedef _Tp& reference;
    typedef const _Tp& const_reference;
    typedef _Tp* pointer;
    typedef const _Tp* const_pointer;
    typedef _RandomContainer random_container_type;
    typedef _MapContainer map_container_type;
    typedef _ScoreCompare score_cmp_type;

public:
    RankingList() {}

    RankingList(const random_container_type &__r)
     : _M_rank_list(__r)
    {
        std::sort(_M_rank_list.begin(), _M_rank_list.end(), score_cmp_type());
        for(std::size_t __i = 0; __i < _M_rank_list.size(); ++__i)
        {
            _M_rank_mapper[_M_rank_list[__i].first] = __i + 1;
        }
    }

    // 重置数据
    void reset(random_container_type &__r)
    {
        _M_rank_list.swap(__r);
        std::sort(_M_rank_list.begin(), _M_rank_list.end(), score_cmp_type());
        for(std::size_t __i = 0; __i < _M_rank_list.size(); ++__i)
        {
            _M_rank_mapper[_M_rank_list[__i].first] = __i + 1;
        }
    }

    void creset(const random_container_type &__r)
    {
        _M_rank_list = __r;
        std::sort(_M_rank_list.begin(), _M_rank_list.end(), score_cmp_type());
        for(std::size_t __i = 0; __i < _M_rank_list.size(); ++__i)
        {
            _M_rank_mapper[_M_rank_list[__i].first] = __i + 1;
        }
    }

    // 数值改变
    void update(const_reference __v, const score_type &__s)
    {
        // 找到对应的名次
        typename map_container_type::iterator __f = _M_rank_mapper.find(__v);
        if(__f == _M_rank_mapper.end())
        {
            _M_rank_list.push_back(std::make_pair(__v, __s));
            std::pair<typename map_container_type::iterator, bool> __p = 
                _M_rank_mapper.insert(std::make_pair(__v, _M_rank_list.size()));
            __f = __p.first;
        }
        else 
        {
            _M_rank_list[__f->second - 1].second = __s;
        }

        score_cmp_type __cmp;

        // 向前比较,找到合适的位置
        while(__f->second > 1 && _M_rank_list.size() > __f->second)
        {
            std::size_t __cur_rank = __f->second;
            std::size_t __former_rank = __cur_rank - 1;
            std::size_t __latter_rank = __cur_rank + 1;

            // 前一名小于我,则我与前一名交换名次
            if(__cmp(_M_rank_list[__former_rank - 1].second, _M_rank_list[__cur_rank - 1].second))
            {
                _M_rank_mapper.insert(std::make_pair(_M_rank_list[__former_rank - 1].first, __cur_rank));
                std::swap(_M_rank_list[__former_rank - 1], _M_rank_list[__cur_rank - 1]);
                __f->second = __former_rank;
            }
            // 我小于后一名,则我与后一名交换名次
            else if(__cmp(_M_rank_list[__cur_rank - 1].second, _M_rank_list[__latter_rank - 1].second))
            {
                _M_rank_mapper.insert(std::make_pair(_M_rank_list[__latter_rank - 1].first, __cur_rank));
                std::swap(_M_rank_list[__cur_rank - 1], _M_rank_list[__latter_rank - 1]);
                __f->second = __latter_rank;
            }
            else break;
        }
    }

    /*
     * 获取名次
     * 如果未进排行榜,则返回0;否则返回实际名次
     */
    std::size_t rank(const_reference __v) const
    {
        typename map_container_type::const_iterator __f = _M_rank_mapper.find(__v);
        return __f == _M_rank_mapper.end() ? 0 : __f->second;
    }

    /*
     * 获取某名次的数据
     */
    const_reference data(std::size_t __rank) const 
    { return _M_rank_list[__rank - 1].first; }

    /*
     * 获取某名次的积分
     */
    const score_type& score(std::size_t __rank) const 
    { return _M_rank_list[__rank - 1].second; }

    /*
     * 排行榜人数
     */ 
    std::size_t size() const 
    { return _M_rank_list.size(); }

    /*
     * 是否存在名次 
     */
    bool exists(std::size_t __rank) const 
    { return !(__rank == 0 || __rank > _M_rank_list.size()); }

    // 获取整个排行榜
    const random_container_type & result() const 
    { return _M_rank_list; }

private:
    random_container_type _M_rank_list; // 排行榜
    map_container_type _M_rank_mapper; // 保存每个key对应的名次
};

#endif // RANKING_LIST_H

测试代码:

#include <iostream>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include "RankingList.h"

typedef RankingList<unsigned long long, long long> rank_list_type;

static void print_rank(const rank_list_type &rank_list)
{
    const rank_list_type::random_container_type &rank = rank_list.result();
    rank_list_type::random_container_type::const_iterator it = rank.begin();
    for(std::size_t i = 1; it != rank.end(); ++it, ++i)
    {
        std::cout << "rank " << i << " uid: " << it->first << "  score:" << it->second << std::endl;
    }
}

static void main_func()
{
    rank_list_type rank_list;

    srand(time(NULL));

    for(int i = 0; i < 10; ++i)
    {
        rank_list.update(i + 1, rand() % 1000);
    }

    print_rank(rank_list);

    std::size_t order = rank_list.rank(10);
    std::cout << "the rank of uid:10 is: " << order << std::endl;

    long long score = rank_list.score(order) / 2;
    rank_list.update(10, score);

    print_rank(rank_list);
}

int main()
{
    main_func();
#ifdef __WIN32
    system("pause");
#endif
    return 0;
}

输出结果:

### 实现实时更新排行榜 为了创建一个能够实时更新排行榜,通常会采用前后端分离的方式。前端负责展示数据并发送请求获取最新的排行情况;后端则处理业务逻辑、存储数据,并提供API接口供前端调用。 #### 使用 WebSocket 进行实时通信 WebSocket 是一种计算机通讯协议,提供了全双工通信渠道,在单个 TCP 连接上进行全双工操作[^1]。通过 WebSocket 可以让服务器主动推送消息给客户端而不需要客户端频繁轮询查询新数据。这对于实现实时性的应用非常有用,比如聊天室、在线游戏中的玩家分数板等场景下的即时刷新显示。 对于本案例而言,可以考虑利用 WebSocket 来建立持久连接,使得每当后台的数据发生变化时(例如新增记录),就能立即通知到所有已订阅该服务的浏览器实例去重新加载最新版本的信息列表。 ```javascript // 前端部分 (Vue.js 中) new Vue({ el: '#app', data() { return { rankingList: [] } }, created() { const socket = new WebSocket('ws://localhost:8080/ws'); socket.onmessage = function(event) { this.rankingList = JSON.parse(event.data); }.bind(this); // 发送初次请求来获得当前的排行榜信息 fetch('/api/rankings') .then(response => response.json()) .then(data => {this.rankingList = data;}); } }) ``` 上述代码片段展示了如何在 Vue 组件内初始化 Web Socket 并监听来自服务器的消息事件。当收到新的排名数据时,它会被解析成 JavaScript 对象并赋值给 `rankingList` 属性以便视图层自动渲染变化后的状态[^2]。 #### 后端支持 Redis 的 ZSet 数据结构管理排行榜 考虑到性能因素,建议使用像 Redis 这样的内存数据库作为缓存机制来保存临时性的排序结果。特别是其有序集合(Sorted Set, zset)非常适合用来维护带权重分值的对象集合作为排行榜的基础模型[^3]。 以下是 Go 语言编写的一个简单例子说明怎样借助于 Redis 更新和读取用户的积分: ```go package main import ( "fmt" "github.com/go-redis/redis/v8" ) func updateScore(client *redis.Client, user string, score float64) error { return client.ZAdd(ctx, "leaderboard", &redis.Z{ Score: score, Member: user, }).Err() } func getTopNUsers(client *redis.Client, n int64) ([]string, error){ result := make([]string, 0) vals, err := client.ZRevRangeWithScores(ctx, "leaderboard", 0, n).Result() if err != nil { return result, fmt.Errorf("failed to retrieve top users: %v", err) } for _, val := range vals { result = append(result, fmt.Sprintf("%s (%f)", val.Member, val.Score)) } return result, nil } ``` 这段程序定义了两个函数用于增加用户得分以及取出指定数量最高分者名单。每次有变动发生时都应该及时同步至 Redis 存储之中,从而保证其他地方访问得到的是最接近实际状况的一份副本[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值