題目描述:你是產品經理,共有 n 個版本 [1, 2, ..., n]。某個版本開始出現錯誤,且之後的所有版本都是壞版本。給定一個 API isBadVersion(version) 可以判斷某個版本是否為壞版本,請在最小化 API 呼叫次數的前提下,找出第一個壞版本。
解題思路:由於版本是線性排列且「好版本全在前、壞版本全在後」,這正是二元搜尋的理想場景。
我們要找的是「第一個 isBadVersion 回傳 true 的版本」,這是一個典型的「搜尋左邊界」二元搜尋:
left = 1,right = n。mid = left + (right - left) / 2。isBadVersion(mid) 為 true:目標可能是 mid 本身或更左邊的壞版本,令 right = mid(注意:不是 mid - 1,因為 mid 本身可能就是答案)。isBadVersion(mid) 為 false:第一個壞版本一定在 mid 右邊,令 left = mid + 1。left == right 時,即找到第一個壞版本。此處迴圈條件使用 left < right(而非 left <= right),配合 right = mid,確保區間持續收縮並在 left == right 時終止,避免無限迴圈。
時間複雜度:O(log n) — 每次迭代將搜尋範圍縮減一半,最多呼叫 ⌈log₂(n)⌉ 次 isBadVersion API,大幅優於線性掃描的 O(n) 次呼叫。
空間複雜度:O(1) — 只使用常數個變數(left、right、mid),不需要額外的資料結構。
1. Set left = 1, right = n
2. While left < right:
a. Compute mid = left + (right - left) / 2
b. If isBadVersion(mid) is true:
- Set right = mid (mid might be the first bad version)
c. Else:
- Set left = mid + 1 (mid is good, search right half)
3. Return left (left == right, pointing to the first bad version)線性掃描 O(n):從版本 1 開始逐一呼叫 isBadVersion,第一個回傳 true 的即為答案。實作最直觀,但呼叫次數最多為 n 次,當 n 很大(例如 2³¹ - 1)時完全不可行。
指數跳躍 + 二元搜尋(適用於 n 未知時)O(log k):若不知道 n 的範圍,可以先以指數步長(1, 2, 4, 8, ...)找到第一個壞版本所在的大致區間,再在此區間內進行二元搜尋。當 n 非常大但第一個壞版本 k 出現很早時,此方法的時間複雜度為 O(log k),優於 O(log n)。
right = mid 而非 right = mid - 1),能解釋這個差異的根本原因嗎?