原文地址:

http://www.limerence2017.com/2019/06/23/cpp01/

做游戏或金融后台开发,经常会遇到设计开发排行榜的需求。比如玩家的充值排行,战力排行等等。而这种排行基本都是即时更新的,快速排序对于单一类型排序可以满足需求,但是对于多种类的排序就很吃力,比如实现一个排行榜,有战力排序,有充值排序,如下图

快速排序的缺陷

如果用快速排序实现,需要定义四种比较规则,而且qsort排序需要一段连续空间,如数组或者vector,为节约内存,每个元素存储玩家基本信息的指针。之后为每种排行类型定义单独的比较规则。代码如下

12345678910111213141516171819202122232425262728293031323334353637383940414243
#include <iostream>#include <vector>#include <algorithm>using namespace std;

struct PowerCmp;struct ChargeCmp;//玩家基本信息class PlayrInfo{public: int getPower()const{ return u_power; }

 int getCharge()const{ return u_charge; }private: int u_ind; //玩家唯一id int u_power; //玩家战力 int u_charge; //玩家充值数量};

bool chargeCmp(PlayrInfo *a, PlayrInfo *b) { return a->getCharge() > b->getCharge();}

bool powerCmp(PlayrInfo *a, PlayrInfo *b) { return a->getPower() > b->getPower();}

int main(int argc, char* argv[]){ //分别实现排序 vector<PlayrInfo*> vecPlayer;

 sort(vecPlayer.begin(),vecPlayer.end(),powerCmp); sort(vecPlayer.begin(),vecPlayer.end(),chargeCmp); getchar(); return 0;}

这么做有几个坏处
1 每个排行都要开辟一段连续的序列,即使存储指针也会造成空间的浪费。
2 qsort适合中小数量排序,当人数上千或万以上会造成瓶颈。但是可以通过插入排序优化。
下面提出一种新的结构来处理排序,boost::multi_index 实现了多索引结构,支持按照多个键值排序,可以极大减轻压力。

multi_index 多索引处理排行榜

multi_index 是boost库提出的多索引结构表,可以设定多个主键进行排序,如战力,充值等等,但是必须要有一个唯一主键,我们将player的id设为唯一主键。
为了方便输出我们先完善下PlayerInfo类,重载输出运算符

123456789101112131415161718192021222324252627
//玩家基本信息class PlayrInfo{public: PlayrInfo(int id, int power, int charge):u_ind(id),u_charge(charge),u_power(power){} int getPower()const{ return u_power; }

 int getCharge()const{ return u_charge; }private: int u_ind; //玩家唯一id int u_power; //玩家战力 int u_charge; //玩家充值数量

 //重载输出 friend std::ostream& operator<<(std::ostream& os,const boost::shared_ptr<PlayrInfo> & e) { //获取引用计数 //cout << e.use_count(); //获取原始指针 //e.get() os<<e->u_ind<<" "<<e->u_power<<" "<<e->u_charge<<std::endl; return os; }};

由于multi_index 各主键排序需要设置比较规则,默认是从小到大,int类型可以用greater, less等。
greater从大到小, less从小到大,也可以自己实现仿函数。

1234567
struct powerOperator{ bool operator()(int a, int b) const { return a > b; }};

定义operator()一定要加上const,因为没有修改参数数据。

multi_index 表定义

基于playerinfo实现multi_index表如下

123456789101112131415
struct by_id;struct by_power;struct by_charge;typedef multi_index_container< boost::shared_ptr<PlayrInfo>, //插入的主体,当然可以是PlayrInfo本身 indexed_by< //唯一主键,不可重复, std::greater<int> 为id规则从大到小,默认从小到大 //tag<by_id>为标签名,可写可不写 ordered_unique<tag<by_id> , const_mem_fun<PlayrInfo,int,&PlayrInfo::getId>>,

 //可重复的主键 ordered_non_unique<tag<by_power>, const_mem_fun<PlayrInfo, int, &PlayrInfo::getPower>, powerOperator >, ordered_non_unique<tag<by_charge>, const_mem_fun<PlayrInfo, int, &PlayrInfo::getCharge>, greater<int> > >> PlayerContainer;

by_id, by_power等都是标签,只需要声明一个结构体,然后tag<标签名>放到索引里。当然可以不带tag,带tag是为了之后获取方便。
powerOperator是定义的战力比较规则,greater定义的充值金额比较规则,是从大到小排序。

multi_index 根据标签获取排序序列

multi_index表在数据插入的时候就根据各个主键排序规则进行排序,所以只需要按照主键取出,获得序列就是排好序的数据。

我们先按照id获取,然后打印输出结果

12
auto& ids = con.get<by_id>();copy(ids.begin(), ids.end(), ostream_iterator<boost::shared_ptr<PlayrInfo> >(cout));

结果如下,可以看出是按照id从小到大排序输出的。

123
1 1231 100002 22222 20003 19999 222222

那我们按照战力获取

12
auto& powers = con.get<by_power>();copy(powers.begin(), powers.end(), ostream_iterator<boost::shared_ptr<PlayrInfo> >(cout));

结果能看出是按照战力从大到小输出

123
2 22222 20003 19999 2222221 1231 10000

multi_index 删除数据

删除元素如果是根据唯一主键删除,可以直接删除,如果是非唯一主键,需要查出所有记录并删除。
删除唯一主键为2的玩家

1234567
auto &itfind = ids.find(2);

  if (itfind != ids.end()){ ids.erase(itfind); cout << "after erase: "<<endl; copy(con.begin(), con.end(), ostream_iterator<boost::shared_ptr<PlayrInfo> >(cout)); }

结果

123
after erase:1 1231 100003 19999 222222

插入一条战力重复的数据,并且遍历删除所有战力为1231的玩家

12345678910
con.insert(boost::make_shared<PlayrInfo>(4, 1231, 10000));auto &beginit = powers.lower_bound(1231);auto & endit = powers.upper_bound(1231);for( ; beginit != powers.end()&& beginit != endit; ){ cout << "...................." <<endl; cout << *beginit; beginit = powers.erase(beginit);}

copy(powers.begin(), powers.end(), ostream_iterator<boost::shared_ptr<PlayrInfo> >(cout));

结果

12345
....................1 1231 10000....................4 1231 100003 19999 222222

lower_bound 获取的是战力为1231的玩家迭代器的起始值,upper_bound获取的是战力为1231的玩家的最大值的下一个元素。

multi_index 修改数据

修改数据可以用replace,也可以用modify
replace 失败不会删除条目,但是二次copy造成效率低下
modify失败会删除对应条目,容易暴力误删除,但是效率高
replace 找到战力为19999的玩家,并且替换为新的数据。

123456
auto piter = powers.find(19999);if(piter != powers.end() ){ auto newvalue = boost::make_shared<PlayrInfo>(100,200,300); powers.replace(piter, newvalue); copy(powers.begin(), powers.end(), ostream_iterator<boost::shared_ptr<PlayrInfo> >(cout));}

下面用modify修改数据

12345678
piter = powers.find(200);  if(piter != powers.end()){       auto newp = 1024; powers.modify(piter, [&](boost::shared_ptr<PlayrInfo> & playptr)->void{ playptr->setPower(newp) ; }); copy(powers.begin(), powers.end(), ostream_iterator<boost::shared_ptr<PlayrInfo> >(cout)); }

modify 第一个参数和replace一样,都是要修改的迭代器,第二个参数是一个函数对象,参数类型为表中元素类型。
到目前为止,multi_index介绍完毕,用该多索引结构可以高效实现多级排序,非常适用于即时排行榜。
源码下载
https://github.com/secondtonone1/boost-multi_index-
我的公众号,谢谢关注

转载于:https://www.cnblogs.com/secondtonone1/p/11107505.html

boost::multi_index 提供一种千人在线即时排行榜的设计思路相关推荐

  1. 抖音直播卡广场如何做到千人在线?

    卡广场技术技术最重要的一点就是利用合作方法去提高观众的停留时间,想要上千人在线的直播间,运气好半小时可以做到,慢点三四天,一般账号只要没有异常情况下,很大概率当天就可以上,每天开播时间越久越好,不管是 ...

  2. 大型多人在线游戏服务器架构设计

    由于大型多人在线游戏服务器理论上需要支持无限多的玩家,所以对服务器端是一个非常大的考验.服务器必须是安全的,可维护性高的,可伸缩性高的,可负载均衡的,支持高并发请求的.面对这些需求,我们在设计服务器的 ...

  3. 26种激发人创造力的天使商标设计

    如果有人委托你为一种产品设计商标,你所要做的第一件事就是找到一个能够做到商标里,并且可以很贴切的表达这种产品的标志.天使是一个很完美的选择,因为它可以用在任何和年轻女孩联系在一起的产品中.因为天使代表 ...

  4. KA算法:一种低复杂度的预编码/接收机设计思路

    前言 这篇博客为 Low-Complexity Statistically Robust Precoder/Detector Computation for Massive MIMO Systems ...

  5. 直播回顾丨Shopee 官方联合!2万点赞!千人在线!全程干货直击跨境小白!

    6月9日,Shopee x 萌啦云课堂「Shopee下半年掘金大促揭秘」直播分享会圆满结束,本场为萌啦云课堂直播首秀,分别在广东省网商协会视频号.萌啦云课堂官网.萌啦科技视频号同步开播. 直播间也邀请 ...

  6. 抖音卡直播广场做到千人在线 有多简单?

    卡广场顾名思义就是卡热门 吧我们的直播间卡到推荐页上 这样别人刷直播很容易刷到我们的直播间 卡上直播广场以后直播间里面就会有人源源不断的进来. 是不是很爽 每个想做直播的都必须要掌握的免费流量玩法. ...

  7. 大型多人在线游戏服务器架构设计 - RPC封装原理

    RPC框架分为客户端部分与服务端部分: RPC-client的部分又分为: (1)序列化反序列化的部分(上图中的1.4) (2)发送字节流与接收字节流的部分(上图中的2.3) 前一篇文章讨论了序列化与 ...

  8. 基于C语言实现的多人在线聊天系统(客户端和服务端源码)

    资源下载地址:https://download.csdn.net/download/sheziqiong/85768602 资源下载地址:https://download.csdn.net/downl ...

  9. KBEngine和Unity3d搭建多人在线游戏(1)

    kbengine环境的搭建 Unity3d及kbengine多人在线游戏的开发 今天和大家一起学习如何使用kbengine及Unity3d搭建一款多人在线游戏,内容设计多个游戏玩家游戏的登录,打怪系统 ...

最新文章

  1. idea插件tomcat8-maven-plugin远程部署项目~
  2. java监控activemq,ActiveMQ与Spring整合-监听消息
  3. 为什么catch了异常,但事务还是回滚了?
  4. vs2015更改默认项目类型
  5. Struts 2的基石——拦截器(Interceptor)
  6. C语言库函数大全及应用实例十四
  7. 游戏基础体验研究:玩家想要什么样的美术品质?
  8. [渝粤教育] 中国地质大学 高层建筑施工 复习题
  9. Docker 安装 MongoDB
  10. select .. into输出单/多行
  11. Spring Session
  12. 山东大学(青岛)博士后招聘-微生物技术国家重点实验室专场
  13. 7.JUC 三大辅助类
  14. 使用python实现往手机发短信(基于twilio)
  15. 输入多个数,中间用空格隔开
  16. 页错误处理 Page Fault Handling
  17. 不知足者常乐:SAP云在中国的落地
  18. 期刊论文图片像素要求
  19. win10安装winpcap4.13
  20. 雷电模拟器frida脱壳

热门文章

  1. matlab中紫色表示什么意思,【紫色代表什么】紫色代表什么含义_紫色代表什么意思_亲亲宝贝网...
  2. 任意文件上传mysql_[代码审计]XiaoCms(后台任意文件上传至getshell,任意目录删除,会话固定漏洞)...
  3. MBR无损转GPT格式
  4. 汇编学习笔记之阶码与移码
  5. AutoHotkey实现windows快捷键设置/映射
  6. 最新Android框架排行榜,上百项资源汇总不容错过
  7. bash使用技巧整理
  8. 电子版微积分,知识共享不走样,原汁原味学微积
  9. Java运算符优先级及结合性
  10. Day01.学习Typora