問題描述
積分排名在很多項目都會出現,積分排名主要滿足以下需求:
- 查詢用戶名次。
- 查詢TopN(即查詢前N名的用戶)
- 實時排名(很多項目是可選的)
當排序的數據量不大的時候,這個需求很容易滿足,但是如果數據量很大的時候比如百萬級、千萬級甚至上億的時候,或者有實時排名需求;這個時候要滿足性能、低成本等需求,在設計上就變得復雜起來了。
解決方案
高效做法是不對積分進行排序,僅僅是統計每個積分區間的人數,用積分區間的形式去統計相應的人數,下面是算法描述。
1. 根據積分范圍創建平衡二叉樹
設[0, N]為積分范圍, 構造的平衡二叉樹如下圖:
每個節點包含兩個數據字段(除了指針):
- Range: 表示積分范圍。
- Counts: 表示當前積分區間包含多少人。
積分的區間的划分是根據平分的方式,把當前積分范圍一分為二生成兩個子節點,然后遞歸的重復該步驟,直到積分區間無法划分為止(即區間[x, y], x == y)
例子:
假設積分范圍為: [0, 5], 構造的平衡二叉樹如下圖:
節點內的數據表示當前積分區間的人數。
從上圖可以看出來,所有積分都在葉子節點,葉子節點即最小粒度的積分區間。
2. 統計相應積分區間的人數
這里主要有兩種操作:
假設積分為i,
添加積分:
添加積分的過程就是查找積分i, 同時累加查找過程經過的節點計數。
下面給出操作例子,注意觀察操作路徑。
例: 需要添加積分3, 結果如下圖
接着在添加積分4,結果如下圖
接着再添加積分4,結果如下圖
接着添加積分2,結果如下圖
刪除積分
刪除積分的過程也是查找積分i, 區別是查找過程經過的節點計數全部減1。
Ps: 只有積分是存在的情況下,才能做刪除操作,另外用一組標記,標識積分是否存在,這里就不列舉了。
例子: 刪除積分4, 結果如下圖
3. 查詢名次操作
查詢某個積分的排名的過程也是查找積分i的過程,下面是查找過程統計節點計數的算法:
對於查找路徑上的任意節點,如果積分在左節點區間,則累加右節點區間的計數。
最終累加計數的結果加1即是積分的名次
例子: 查找積分3的名次
藍色節點是查找積分3經過的路徑,紅色節點是需要累加的計數值。
最終結果是:0 + 1 + 1, 積分3的名次是第2名
從上面的算法可以看出,對平衡二叉樹的操作,算法復雜度是O(log N), N是最大積分。
在積分范圍不變的情況下,算法復雜度是穩定的,跟用戶量無關,因此可以實現海量用戶積分排名、實時排名算法需要。
對於海量積分數據實時排名、這里給出的是核心算法,實際業務的時候還需要增加一些額外的處理,比如uid於積分的映射表用於記錄用戶歷史積分、積分與uid的映射表用於TopN這種查詢前N名的需求、數據持久化、高可用等需求。