網傳 Pinterest 的面試題很難,真做下來其實還好——題目不刁鑽,但非常注重程式碼品質、溝通清晰度和 ownership。整個 Virtual Onsite 由 三輪 coding + 一輪 Hiring Manager 溝通 組成,coding 不追求 trick,而是看你能否寫出健壯、可擴展、貼近真實業務的程式碼。下面把三道真題完整複盤一遍,思路都很有代表性。
一、Pinterest SDE VO 概覽
| 維度 | 詳情 |
|---|---|
| 輪次 | 3 輪 coding + 1 輪 HM competency |
| 形式 | 遠端視訊,注重 verbal reasoning |
| 難度 | 中等,題不偏,但 follow-up 深 |
| 考點 | 圖 / 狀態管理 / 資料存取介面設計 |
| 風格 | 工程化,coding 常夾帶 design 元素 |
核心提醒:Pinterest 的題目大多和實際系統設計掛鉤,變數命名、邊界條件、擴展性 是評分重點。系統設計雖然沒有單獨一輪,但 coding 裡經常埋 design 元素,trade-off 要準備好深入講。
二、題 1:最多翻轉 K 個開關的可達性(變種 BFS)
給定一張城市地圖,每條邊代表一條單向道路,每條道路有一個開關(on/off)。問從城市 A 到城市 B 是否存在一條路徑,最多允許翻轉 K 個開關使道路變為可通行。
思路轉化:經典最短路的狀態是「目前節點」,這題把狀態擴展為 (目前節點, 已翻轉次數)。每遇到一條關閉的道路,如果翻轉次數還沒超過 K,就 +1 加入佇列;遇到開著的道路則不消耗預算。用 visited[node][flips] 去重,避免重複造訪同一狀態。
from collections import deque
def can_reach(n, edges, A, B, K):
# edges[u] = list of (v, is_open);is_open=True 表示道路本來就通
graph = [[] for _ in range(n)]
for u, v, is_open in edges:
graph[u].append((v, is_open))
# 狀態 = (節點, 已翻轉開關數)
start = (A, 0)
visited = {start}
q = deque([start])
while q:
node, flips = q.popleft()
if node == B:
return True
for nxt, is_open in graph[node]:
cost = 0 if is_open else 1
nf = flips + cost
if nf <= K and (nxt, nf) not in visited:
visited.add((nxt, nf))
q.append((nxt, nf))
return False
關鍵點:面試官特別在意 flip 計數邏輯能否避免重複造訪——同一個節點用不同的翻轉次數抵達是不同狀態,不能只按節點去重,否則會漏解或死迴圈。時間複雜度:O((V+E)·K),空間複雜度:O(V·K)。
三、題 2:帶驅逐回呼的 LRU Cache
在標準 LRU Cache 的
get/put之外,要求支援:當某個元素因為容量上限被驅逐時,觸發一個 callback 函式。
思路轉化:底層結構還是 雙向鏈結串列 + 雜湊表,難點在於「驅逐時機」——只有在 put 導致超容、需要淘汰尾部節點時,才呼叫 callback,並且要在刪除完成後呼叫,保證回呼裡讀到的狀態是一致的。
class Node:
__slots__ = ("key", "val", "prev", "next")
def __init__(self, key=0, val=0):
self.key, self.val = key, val
self.prev = self.next = None
class LRUCacheWithEviction:
def __init__(self, capacity, on_evict=None):
self.cap = capacity
self.on_evict = on_evict # 驅逐回呼
self.cache = {}
self.head, self.tail = Node(), Node()
self.head.next, self.tail.prev = self.tail, self.head
def _remove(self, node):
node.prev.next, node.next.prev = node.next, node.prev
def _add_front(self, node):
node.prev, node.next = self.head, self.head.next
self.head.next.prev = node
self.head.next = node
def get(self, key):
if key not in self.cache:
return -1
node = self.cache[key]
self._remove(node)
self._add_front(node)
return node.val
def put(self, key, val):
if self.cap <= 0:
return
if key in self.cache:
self._remove(self.cache[key])
node = Node(key, val)
self.cache[key] = node
self._add_front(node)
if len(self.cache) > self.cap:
lru = self.tail.prev
self._remove(lru)
del self.cache[lru.key]
if self.on_evict: # 驅逐後再回呼
self.on_evict(lru.key, lru.val)
寫完後面試官追問了 執行緒安全(演算法面裡問這個屬於意外):是否需要加鎖、是否支援並行存取,以及「如果 callback 執行時間太長會不會卡主流程」。我提到可以把回呼丟到非同步佇列裡執行,避免阻塞 put。時間複雜度:get / put 均攤 O(1)。
四、題 3:日誌存取記錄查詢類別設計
輸入是日誌存取記錄
(user_id, action_type, timestamp),實作一個類別支援兩個方法:countUniqueUsers(start, end)統計時間區間內的去重使用者數;getUserActions(user_id, start, end)回傳某使用者在區間內的所有行為。
思路轉化:典型的「索引結構 + 區間查詢」設計題。按 user_id 建一個索引方便取單一使用者行為,按 timestamp 排序便於區間二分。getUserActions 直接在該使用者的有序串列上二分定位區間;countUniqueUsers 在全域有序事件上掃窗並用集合去重。
import bisect
from collections import defaultdict
class LogStore:
def __init__(self):
self.by_user = defaultdict(list) # user_id -> [(ts, action)] 有序
self.events = [] # [(ts, user_id)] 全域有序
def record(self, user_id, action_type, ts):
# 假設按時間近似有序抵達,必要時插入保持有序
self.by_user[user_id].append((ts, action_type))
bisect.insort(self.events, (ts, user_id))
def getUserActions(self, user_id, start, end):
arr = self.by_user.get(user_id, [])
lo = bisect.bisect_left(arr, (start,))
hi = bisect.bisect_right(arr, (end, chr(0x10FFFF)))
return [a for _, a in arr[lo:hi]]
def countUniqueUsers(self, start, end):
lo = bisect.bisect_left(self.events, (start, 0))
hi = bisect.bisect_right(self.events, (end, float("inf")))
return len({uid for _, uid in self.events[lo:hi]})
面試官非常關心 資料量大時的效能,還追問「能不能只用一個索引結構同時支撐兩個方法」——這部分討論挺深入,本質是在權衡空間換時間。複雜度:getUserActions O(log n + k),countUniqueUsers O(log n + m)。
五、第四輪:Hiring Manager Competency
最後一輪不考演算法,重點聊 experience 和合作經歷。HM 先介紹團隊在做的 infra 工作,然後問:
- 有沒有從零設計過某個系統?
- 是否帶過新人 / 做過 mentoring?
- 有沒有對 legacy code 做過重構、怎麼推動團隊 adopt 新工具?
我重點講了一個非同步訊息系統從 design 到 rollout 的完整過程,以及如何推動整個團隊遷移。整個對話很輕鬆,但 HM 在評估你的 溝通方式和影響力。
六、備考重點
| 能力 | 怎麼練 |
|---|---|
| 狀態建模 | 把「節點」擴展成「節點 + 附加狀態」,練 BFS/Dijkstra 變種 |
| 介面設計 | 練帶狀態管理、資料存取 API 的設計題,想清楚索引結構 |
| Trade-off 表達 | 每個方案主動說出 2 個備選 + 取捨理由 |
| HM 故事 | 準備 system 從設計到落地、mentoring、推動變革三類故事 |
FAQ
Q1:Pinterest SDE VO 一共幾輪?
三輪 coding + 一輪 Hiring Manager 溝通。三輪 coding 都注重程式碼品質和邊界處理,HM 輪聊合作經歷與影響力,重點評估 culture fit 和 ownership,不考演算法。
Q2:Pinterest 的題真的很難嗎?
網傳很難,實際中等。題目不刁鑽、不追求 trick,但 follow-up 很深,而且經常在 coding 裡夾帶系統設計元素。比起 trick,面試官更看重變數命名、邊界條件和擴展性。
Q3:演算法面會問執行緒安全嗎?
會,而且常出乎意料。比如做 LRU 時被追問是否需要加鎖、callback 太慢會不會阻塞主流程。提前準備「非同步處理 / 加鎖粒度 / 並行存取」的回答能加分。
Q4:Pinterest VO 怎麼準備性價比最高?
多練貼近實際業務的題型,尤其是帶狀態管理或資料存取介面的設計題。如果想要這三道真題的限時陪練、變種拓展,或需要 VO 輔助 / VO 代面 的即時對接,可以發職缺 JD 先做題型預測再排練習計畫。
正在準備 Pinterest SDE VO?
Pinterest 考的是工程化的程式碼品質和系統思維,不是死刷題。oavoservice 提供 Pinterest / Meta / 頭部網路公司的 VO 全流程陪練:三輪 coding 限時模擬、HM competency 故事打磨、設計題 trade-off 拆解,也支援 VO 輔助 / VO 代面 的即時對接。教練含前大廠資深工程師,熟悉 Pinterest「coding 夾帶 design」的評分風格。
立即新增微信 Coding0201,獲取 Pinterest VO 真題與陪練。
聯絡方式
- 微信:Coding0201
- Email:[email protected]
- Telegram:@OAVOProxy