← 返回部落格列表 Google SDE R1 兩輪複盤:Coding + BQ 深挖
Google

Google SDE R1 兩輪複盤:Coding + BQ 深挖

2026-08-12

這篇複盤記錄我在 Google SDE 職位 R1 的兩個輪次:一輪 Coding,一輪純 BQ,各 45 分鐘。除了題目本身,我也把 Onsite 當天的差旅報銷、進出辦公區的流程這些瑣碎但實用的細節一起整理了下來。如果你正在準備 Google 的現場面試,希望這份筆記能幫你把節奏和心態都調到位。

面試概覽

R1 一共兩輪,節奏很標準:第一輪先聊人再寫程式碼,第二輪全程 BQ。兩位面試官背景不同,風格也明顯不一樣。

輪次 時長 形式 考點
Round 1(Coding) 45 分鐘 自我介紹 + 簡歷專案聊天 + 現場寫程式碼 專案深度、Coding 思路、區間劃分建模
Round 2(BQ) 45 分鐘 純行為面試,專案與協作深挖 STAR 結構、判斷過程、團隊協作

一個體感:Coding 輪的前半段聊專案並不是走過場,面試官會順著你的回答追問「具體你做了什麼」;BQ 輪更是全程圍繞你的真實經歷深挖,套模板的答案很容易被問穿。


Round 1:Coding 輪

第一輪的面試官是位母語中文的工程師,全程節奏比較放鬆。開場是自我介紹,接著圍繞簡歷聊專案:為什麼選擇 Google、專案裡最大的挑戰是什麼、你是怎麼解決的。這一段建議提前把一兩個專案的技術細節和你個人的貢獻理清楚,因為面試官會追著細節問,而不是聽你複述 JD。

聊完專案大概過了三分之一時間,進入 Coding。

題目:租車訂單排班

給定一組租車訂單,每個訂單有訂單號、取車時間和還車時間。要求把訂單分配到若干輛車上,使得同一輛車上的訂單在時間上互不重疊;並設計一個 Car 類別,持有這輛車以及分配到它身上的訂單列表。

這本質上是一道**區間劃分(interval partitioning)**問題:目標是用盡量少的車覆蓋所有訂單。

思路

  1. 把所有訂單按取車時間升序排序。
  2. 維護一個最小堆,堆裡放每輛車「最早可用時間」(也就是該車當前最後一單的還車時間)。
  3. 依次處理每個訂單:看堆頂那輛車——如果它的還車時間 <= 當前訂單的取車時間,說明這輛車已經空出來了,直接複用;否則所有車都還在被佔用,只能新開一輛車。
  4. 分配完後更新這輛車的可用時間,把它重新壓回堆裡。

關鍵點在 dry run 時要主動說清楚邊界:還車時間恰好等於下一單取車時間時算不算衝突? 這要跟面試官確認口徑——通常認為「上一單還了才能取下一單」,即 <= 視為可複用。

Python 解法

import heapq
from typing import List


class Order:
    """一條租車訂單:訂單號、取車時間、還車時間。"""

    def __init__(self, order_id: str, pickup: int, ret: int):
        self.order_id = order_id
        self.pickup = pickup
        self.ret = ret


class Car:
    """一輛車,持有分配到它身上的訂單列表。"""

    def __init__(self, car_id: int):
        self.car_id = car_id
        self.orders: List[Order] = []

    @property
    def available_at(self) -> int:
        """該車最早可用時間:最後一單的還車時間,無單則視為負無窮。"""
        return self.orders[-1].ret if self.orders else float("-inf")

    def assign(self, order: Order) -> None:
        self.orders.append(order)


def assign_orders(orders: List[Order]) -> List[Car]:
    """把訂單分配到盡量少的車上,同一輛車訂單時間不重疊。"""
    # 邊界情況:沒有訂單直接返回空
    if not orders:
        return []

    # Step 1: 按取車時間升序排序
    orders.sort(key=lambda o: o.pickup)

    cars: List[Car] = []
    # 最小堆元素為 (最早可用時間, 車在 cars 中的下標)
    heap: List[tuple] = []

    for order in orders:
        # Step 2: 堆頂車已空出(還車時間 <= 當前取車時間)則複用
        if heap and heap[0][0] <= order.pickup:
            _, idx = heapq.heappop(heap)
            car = cars[idx]
        else:
            # Step 3: 否則新開一輛車
            car = Car(car_id=len(cars))
            cars.append(car)
            idx = car.car_id

        # Step 4: 分配後更新可用時間並壓回堆
        car.assign(order)
        heapq.heappush(heap, (order.ret, idx))

    return cars

時間複雜度:O(n log n),排序與堆操作都是 log 級。 空間複雜度:O(n),堆與車輛列表最多存放 n 個元素。

Dry run 演示

用訂單 [(A, 1, 4), (B, 2, 5), (C, 4, 6), (D, 7, 8)](格式為 訂單號, 取車, 還車)走一遍:

步驟 當前訂單 堆頂可用時間 決策 車輛狀態
處理 A(1,4) A 堆空 新開 Car0 Car0=[A]
處理 B(2,5) B 4 > 2 新開 Car1 Car0=[A], Car1=[B]
處理 C(4,6) C 4 <= 4 複用 Car0 Car0=[A,C], Car1=[B]
處理 D(7,8) D 5 <= 7 複用 Car1 Car0=[A,C], Car1=[B,D]

最終兩輛車即可覆蓋:Car0 裝 A、C,Car1 裝 B、D。

主動補充要驗證的邊界:

Follow-up:如果最多只有 K 輛車?

面試官接著問:如果車隊規模有上限,最多只有 K 輛車怎麼辦?這題的重點不在程式碼,而在先澄清業務需求。我當時是這樣拆的:

實作上,可以把堆容量封頂在 K:當堆裡已有 K 輛車且堆頂車仍未空出時,就觸發上面的拒單 / 失敗邏輯。這裡我沒有急著寫完整程式碼,而是把權衡講清楚,讓面試官看到我在動手前會先對齊需求。


Round 2:BQ 輪

第二輪的面試官來自印度,全程純 BQ,圍繞簡歷專案和團隊協作深挖。問題都很經典,但追問很細:

  1. 當隊友一開始不認同你的方案時,你怎麼說服 TA?
  2. 多個任務同時搶佔資源時,你如何排優先級?
  3. 當一個計劃外的新機會出現,你會不會暫停手頭的工作去追它?你是怎麼判斷的?

我的體會是,BQ 輪拿分的關鍵不是「答案漂亮」,而是講清楚判斷過程


其他 R2 Onsite 相關資訊 / logistics

除了題目,Onsite 當天有不少流程和報銷的細節,提前知道能省很多心:


備考建議

Google 的 R1 這兩輪,考的其實是兩種不同的能力,準備時要分開打磨。


FAQ

Q1:Coding 輪前面聊專案重要嗎?

很重要,不是走過場。面試官會順著你的回答追問「具體你做了什麼、怎麼解決的」。提前把一兩個專案的技術細節和個人貢獻理清楚,比背 JD 有用得多。

Q2:租車訂單排班為什麼用最小堆,而不是每來一單遍歷所有車?

遍歷所有車找空車是 O(n²)。用最小堆維護每輛車的「最早可用時間」,只需看堆頂就能判斷能否複用,整體 O(n log n),在資料量大時優勢明顯,也更好口頭解釋。

Q3:還車時間恰好等於下一單取車時間,算衝突嗎?

取決於業務口徑,要主動跟面試官確認。常見約定是「上一單還了才能取下一單」,即還車時間 <= 取車時間視為可複用,程式碼裡用 <= 判斷即涵蓋這種端點相接的情況。

Q4:「最多 K 輛車」的 follow-up 該怎麼答?

先澄清需求:如果所有訂單必須滿足,需要第 K+1 輛車時就返回失敗;如果允許拒單,再問目標是保留高價值訂單還是最大化訂單數量,不同目標對應不同策略。把權衡講清楚比直接寫程式碼更重要。

Q5:BQ 輪怎麼答才不容易被問穿?

用真實的 STAR 故事,重點講判斷過程而非結論:說清你面臨的約束、你個人具體做了什麼、最後結果如何。編的故事在第二層追問時很容易露餡。


正在準備 Google SDE 的 Coding 與 BQ 輪? 我們熟悉這類區間建模題的講法和 BQ 深挖的節奏,能陪你把建模思路和 STAR 故事都打磨到經得起追問。

立即添加微信 Coding0201獲取一對一定制備考方案

聯絡方式