实时更新的排行榜

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

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

实现

用一个支持随机访问的容器(如动态数组)保存用户数据(包括用户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;
}

输出结果:

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值