每個複雜性等級都畫出一條曲線
根據輸入大小繪製成本
將輸入大小 N 放在 x 軸上。將成本(操作、時間)放在 y 軸上。每個複雜性等級都產生一條不同的曲線。您可以從性能曲線的形狀識別算法的增長率。
O(1) — 平坦的水平線。 函數 f(N) = 1。沒有斜率。無論 N 是多少,成本都不變。哈希表查找:無論表包含 10 還是 10,000,000 個元素,一次哈希計算都會跳到正確的桶。
O(log N) — 溫和向上的曲線,斜率遞減。 在 N = 1,000,000 時:成本 ≈ 20 次操作。曲線在小 N 時陡峭上升,然後變平。N 的每次加倍都增加恰好一個單位的成本。
O(N) — 對角直線。 斜率 = 1(漸近意義上)。成本以與 N 相同的速率增長。如果 N 增加三倍,成本也增加三倍。
O(N log N) — 更陡峭的對角線,略有向上的曲率。 位於 O(N) 和 O(N²) 之間。對數因子為線性斜率添加了一個緩慢增長的乘數。
O(N²) — 向上開口的拋物線。 斜率隨著 N 增加而增加。在 N = 10 時:成本 = 100。在 N = 100 時:成本 = 10,000。在 N = 1,000 時:成本 = 1,000,000。
不斷增長的差距
比率 O(N²) / O(N) = N。隨著 N 增加,拋物線和對角線之間的差距擴大。在 N = 10 時:10 倍差距。在 N = 100 時:100 倍差距。在 N = 1,000 時:1,000 倍差距。在 N = 50,000 時:50,000 倍差距。差距總是等於 N。
計算規模差距
一個大型依賴關係圖包含 50,000 個包(N = 50,000)。一個算法在 O(N) 時間內運行。第二個在 O(N²) 時間內運行。在這個 N 處,比率 O(N²)/O(N) = N = 50,000。
平分線段
二分查找作為重複二分法
N 個元素的排序數組形成長度為 N 的線段。二分查找重複地將此段進行二分:
1. 指向剩餘段的中點。
2. 如果目標 < 中點:右半部分消失。對左半部分遞歸。
3. 如果目標 > 中點:左半部分消失。對右半部分遞歸。
4. 如果目標 = 中點:完成。
在 k 次二分法後,剩餘段的長度為 N / 2^k。當該長度等於 1 時搜索結束:
N / 2^k = 1 → 2^k = N → k = log₂N
因此,對 N 個元素進行二分查找最多需要 log₂N 次比較。
二分與加倍:同一曲線的兩個側面
二分和加倍是幾何逆運算。指數曲線 2^k 和對數曲線 log₂N 是相對於直線 y = x 的鏡像。
考慮紙張折疊:一張紙開始厚度為 0.1 毫米。每次折疊都會使厚度加倍。42 次折疊後:0.1 毫米 × 2^42 ≈ 439,804 公里 — 超過月球(384,400 公里)。二分查找運行相反的過程:從 N 個元素開始,每一步將計數減半,在 log₂N 步中達到 1 個元素。
幾何學:二分法是一種自相似操作。每次二分都會產生兩個在結構上與整體相同的一半。遞歸和對數共享這種自相似性。
比較與加倍
排序數組包含 1,048,576 個元素。注意:1,048,576 = 2^20。
哈希函數作為幾何映射
哈希函數作為函數
哈希表使用哈希函數將鍵映射到桶:
bucket = hash(key) mod table_size
這在嚴格的數學意義上是一個函數:它將定義域(所有可能的鍵)映射到值域(桶索引 0 到 table_size − 1)。幾何圖像:鍵佔據一個空間;桶佔據另一個空間。哈希函數將鍵投影到桶索引上。
O(1) 查找 — 直接跳轉,無搜索。 哈希函數在常數時間內計算桶索引。直接跳轉到該桶。無遍歷,無比較循環。
負載因子。 在負載因子 0.75 時,75% 的桶至少包含一個元素。在 ~0.9 以上,碰撞增加:兩個鍵哈希到同一個桶,在該桶內形成元素鏈。在長鏈中的查找在最壞情況下降級為 O(N)。
分佈質量作為幾何學
分佈良好的哈希函數在所有桶中均勻分散鍵。幾何上:函數的值域均勻覆蓋其陪域。每個桶大約接收 N / table_size 個鍵。
分佈不良的哈希函數將鍵聚集到幾個桶中。幾何上:函數的值域折疊到陪域的子集 — 大多數鍵映射到相同的幾個點。剩餘的桶為空。
與 MOAD-0001 的連接
用集合替換列表可以修復 MOAD-0001,因為集合在內部使用哈希表。O(N) 列表掃描 → O(1) 哈希表查找。幾何上:沿著序列的線性遍歷轉變為通過函數的直接投影。序列消失;函數替換了它。
分析分佈不良的哈希
哈希表有 1,000 個桶和 900 個元素(負載因子 0.9)。一個分佈不良的哈希函數將其中 500 個元素發送到同一個桶。