1. 程式人生 > >用跳錶實現遊戲排行榜

用跳錶實現遊戲排行榜

排行榜有很多種設計方案:

比如陣列,排序樹,Redis的sort set等,還有這裡說的跳錶。

先科普一下跳錶以及分析一下跳錶優劣:

跳錶:在普通連結串列中,給一些節點增加額外的指標,使得這些節點能夠一次跨越更多的中間節點,提高了效率。

優點:相比普通連結串列,由於跳躍的特性,可以節省便利次數,時間複雜度上是O(logN)。相比平衡二叉樹,在插入和刪除操作上,不需要再進行樹的平衡等操作。

缺點:有額外指標的空間消耗,在資料量超大的情況下,效能還是會下降。

跳錶結構如下:

每個框框表示一個Node,裡面存了一個指標陣列,位數越大,跨度越大

先說一下查詢

由於位數越大,跨度越大,所以我們從位數高到位數低遍歷,對每一位:While迴圈對比指標指向的Node裡的Score是否小於插入成績,是則指標跳到該Node,否則跳出迴圈。

uint32 GetRank(int64 InsertCode, uint64 InsertScore)
{
  if(!insertCode)
    return;
  skiplistNode* pNode = header;
  uint32 rank = 0;
  for(int i = CurLevel - 1; i >= 0; i--)
  {
    skiplistNode* pNexNode = pNode->level[i].forward;
    while(pNexNode && pNexNode->Value<=InsertValue)
    {
      rank 
+=pNode->level[i].span; pNode = pNode->level[i].forward; } if(pNode->Code == InsertCode) return rank; } return 0; }

能夠看懂查詢的話,插入和刪除也就差不多了,理一下插入流程

1,整體流程是:找到插入位置,記錄指標陣列,隨機一個插入Node的Level,然後插入(與一般的連結串列插入差不多)

2,建立指標陣列和排名陣列,比如:skiplistNode* update[MaxLevel]; uint32 rank[MaxLevel];

3,查詢。這兒查詢需要記錄最接近插入位置的每一個跨度的指標,儲存到update裡

4,隨機一下插入Node的Level,通常建議:level = 1; while(1/4概率) ++level; 這樣跨度等級不會爆炸

5,如果InsertLevel超過了當前排行榜的Level,就把update陣列中超出部分的指標指向header,且每位儲存的步長都設定為排行榜的長度Length

6,接著就是常規連結串列的插入了,這兒相當於同時插入了InsertLevel個連結串列,根據update陣列中的指標,和指標指向的下一個Node,中間插入

7,插入之後,遍歷update陣列,將裡面的InsertLevel位到排行榜CurLevel位的步長加1

8,因為這種設計的排行榜最後面的是最大值,我們在每個Node裡存一個後向指標,組成雙向連結串列,方便遍歷,所以再簡單處理一下Node裡的backward指標就可以了

刪除的操作和插入差不多,從這一系列的步驟來看,對比平衡樹在插入和刪除之後,還要調整位置,跳錶就不需要這麼麻煩了,所以這是一個很好的可以用來設計排行榜的方式。

對了,如果需要在排行榜上儲存玩家資料怎麼弄?儲存在Node裡?

不建議直接存Node裡,不然其他地方需要資料的時候還需要查詢一次,建議Node裡存一個唯一標誌的Code和成績就可以了,玩家詳細資訊用一個HashMap儲存在其他地方(如果有多個排行榜的話,還可以共用資料)