← 返回博客列表
Robinhood

Robinhood 上岸必刷|Referral Leaderboard:鏈式推薦與 DFS

2025-08-17

導讀:Robinhood 的核心增長引擎是它的 Referral Program(推薦計劃)。這道題非常經典,因為它將業務邏輯(裂變拉新)完美映射到了演算法模型(樹/森林的子樹統計)。

很多同學在 OA 或 VO 遇到這題時,容易在「間接推薦」的定義上卡殼,或者寫出 $O(N^2)$ 的暴力解法。今天 oavoservice 帶你拆解這道 Top 3 Leaderboard 的 O(N) 最優解。

題目復盤:Referral Leaderboard

業務場景

你需要構建一個看板,追蹤用戶的推薦影響力。邏輯不僅僅是「我拉了誰」,而是「由於我,整個鏈條進來了多少人」。

如果 A 推薦了 B,B 推薦了 C,C 推薦了 D: A → B → C → D

在這個模型下:

核心需求

給定兩組陣列 rh_users(推薦人)和 new_users(被推薦人),輸出 推薦貢獻榜 Top 3

規則細節(坑點):

  1. 一次性原則:每個用戶只能被推薦一次(意味著結構是 森林,不會有環,也不會有多個父節點)。
  2. 排序規則
    • 優先按 推薦總數 降序。
    • 數量相同時,按 Username 字母序 升序。
  3. 門檻:只有推薦了至少 1 人的用戶才有資格上榜。

演算法破局:從暴力到優雅

❌ 陷阱:暴力模擬

最直覺的做法是:對於每一個用戶,順著鏈條往下 while 迴圈數人頭。

✅ 正解:DFS + Memoization(後序遍歷)

這本質上是一個 「計算多叉樹子樹大小」 的問題。 一個節點(用戶)的推薦總數 = 所有子節點(直推用戶)的貢獻之和 + 子節點本身的數量

思路步驟:

  1. 建圖:使用雜湊表(Adjacency List)構建 referrer -> [referees] 的映射。
  2. 記憶化搜尋:定義 dfs(user) 返回該用戶「鏈條下」的總人數。
    • count = 0
    • for child in children: count += 1 + dfs(child)
    • 存入 memo,避免重複計算。
  3. 排序輸出:利用 Python 的 Tuple 排序特性 (-count, username) 完美解決 Tie-break 問題。

Python 滿分程式碼模板

from collections import defaultdict

def top_3_referrals(rh_users, new_users):
    # 1. 建圖:Adjacency List (Directed Tree/Forest)
    graph = defaultdict(list)
    # 使用集合收集所有出現過的人,確保沒有漏掉任何潛在的 Referrer
    all_users = set(rh_users) | set(new_users)
    
    for r, n in zip(rh_users, new_users):
        graph[r].append(n)
    
    # 2. 記憶化搜尋 (DFS)
    memo = {}

    def get_referral_count(node):
        if node in memo:
            return memo[node]
        
        total_descendants = 0
        for child in graph[node]:
            # 貢獻 = 子節點本身(1) + 子節點拉來的人(get_referral_count(child))
            total_descendants += 1 + get_referral_count(child)
            
        memo[node] = total_descendants
        return total_descendants

    # 3. 計算所有人的貢獻
    candidates = []
    for user in all_users:
        count = get_referral_count(user)
        if count > 0:
            candidates.append((count, user))
            
    # 4. 排序:數量降序(-count),字母升序(user)
    # sort 默認是升序,所以數量取負值來實現降序
    candidates.sort(key=lambda x: (-x[0], x[1]))
    
    # 5. 格式化輸出 Top 3
    result = []
    for count, user in candidates[:3]:
        result.append(f"{user} {count}")
        
    return result

# Test Case
if __name__ == "__main__":
    rh = ["A", "B", "C"]
    new = ["B", "C", "D"]
    print(top_3_referrals(rh, new)) 
    # Output: ['A 3', 'B 2', 'C 1'] 

需要面試真題? 立刻聯繫微信 Coding0201獲得真題