白板程式設計:斐波那契數列實戰#
以斐波那契數列為例,展示如何在面試中從暴力解逐步優化到最優解。
解法演進#
1. 暴力遞迴 $O(2^n)$#
最直觀的解法,直接定義 $f(n) = f(n-1) + f(n-2)$:
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)存在大量重複計算。例如計算 $f(5)$ 需要計算 $f(4)$ 和 $f(3)$,而計算 $f(4)$ 時又要計算一次 $f(3)$。當 $n=40$ 時,程式執行速度將變得極慢。
2. 記憶化遞迴 $O(n)$#
加上快取,避免重複計算:
def fib(n, memo={}):
if n <= 1:
return n
if n not in memo:
memo[n] = fib(n-1, memo) + fib(n-2, memo)
return memo[n]3. 動態規劃 $O(n)$#
正推方式,只保留前兩個狀態:
def fib(n):
if n <= 1:
return n
prev, curr = 0, 1
for _ in range(2, n + 1):
prev, curr = curr, prev + curr
return curr4. 矩陣快速冪 $O(\log n)$#
斐波那契數列求第 $n$ 項的最優解法是矩陣快速冪,時間複雜度為 $O(\log n)$。
矩陣快速冪原理#
構造狀態矩陣#
找到常數矩陣 $M$,使得:
$$ \begin{bmatrix} F_n \ F_{n-1} \end{bmatrix} = M \times \begin{bmatrix} F_{n-1} \ F_{n-2} \end{bmatrix} $$
根據斐波那契定義:
- $F_n = 1 \cdot F_{n-1} + 1 \cdot F_{n-2}$
- $F_{n-1} = 1 \cdot F_{n-1} + 0 \cdot F_{n-2}$
得到常數矩陣:
$$ M = \begin{bmatrix} 1 & 1 \ 1 & 0 \end{bmatrix} $$
轉化為冪運算#
展開遞推:
$$ \begin{bmatrix} F_n \ F_{n-1} \end{bmatrix} = M^{n-1} \times \begin{bmatrix} F_1 \ F_0 \end{bmatrix} $$
求解 $F_n$ 轉化為求解 $M^{n-1}$,套用快速冪演算法即可達到 $O(\log n)$。
升維思想:在一維遞推中只能達到 $O(n)$。引入矩陣後,利用數學結構「摺疊」計算過程,實現指數級加速。
快速冪演算法#
核心思想#
利用 $x^n = x^{n/2} \times x^{n/2}$ 的性質,將規模減半。
二進位制迭代法(推薦)#
將 $n$ 視為二進位制串。例如 $x^{10}$,$10$ 的二進位制是 1010,即 $x^8 \times x^2$。
double myPow(double x, int n) {
long long N = n;
if (N < 0) {
x = 1 / x;
N = -N;
}
double result = 1.0;
double current_product = x;
while (N > 0) {
if (N & 1) { // 當前位元是 1
result *= current_product;
}
current_product *= current_product; // 權重翻倍
N >>= 1; // 處理下一位
}
return result;
}位元運算技巧:
n & 1判斷奇偶(等價於n % 2 == 1)n >> 1右移一位(等價於n / 2)
面試中的 Clarification#
遇到 pow(x, n) 這類題目,務必先確認邊界條件:
- $n$ 可以是負數嗎?
- $x$ 可以是 $0$ 嗎?($0$ 的負數次方會導致除以零)
- 需要處理整數溢位嗎?(如 $n = \text{INT_MIN}$ 時,$-n$ 會溢位)
Clarification 比直接寫出程式碼更重要。這展示了你的工程思維與嚴謹性。
遞迴模板#
面試中使用遞迴時,遵循以下四步驟:
- Terminator:終止條件
- Process:處理當前層邏輯
- Drill Down:下探到下一層
- Clear Status:清理當前層狀態(若需要)
能夠看出程式碼的「壞味道」並進行重構,是工程師進階的必經之路。例如將
n % 2優化為n & 1,不僅是效能提升,更是對底層邏輯理解的展現。