位運算是 C 語言贴近硬件的核心優勢。本文解析兩個經典面試/工程高頻算法:**統計整數二進制中 1 的個數 (Hamming Weight)** 與 **判斷是否為 2 的冪次**,核心均利用 `x & (x-1)` 這一「消除最低位 1」的神奇特性。
x = 10011100001111 (9999)
x - 1 = 10011100001110
x & (x-1) = 10011100001110 ← 最低位 1 (第0位) 變 0
再執行一次:
x = 10011100001110
x - 1 = 10011100001101
x & (x-1) = 10011100001100 ← 原第1位 1 變 0
...
循環 8 次後 x 變 0 → 共 8 個 1
原理:減 1 會將最低位 1 變 0,並將其右側所有 0 變 1。與原數 `&` 運算,最低位 1 變 0,右側全 0,高位不變。
int count_ones(int x) {
int count = 0;
while (x) {
x &= (x - 1); // 消除最低位 1
count++;
}
return count;
}
/* 測試 */
count_ones(9999) → 8 // 10011100001111 有 8 個 1
count_ones(0) → 0
count_ones(-1) → 32 (32位系統,全 1)
複雜度:O(k),k 為 1 的個數。**最壞 O(32/64) = O(1)**。比遍歷 32 位 (O(32)) 更快,尤其稀疏 1 時極其高效。
數學特徵:2 的冪次二進制**有且僅有最高位為 1,其餘全 0**。
2^0 = 1 = 0001
2^1 = 2 = 0010
2^2 = 4 = 0100
2^3 = 8 = 1000
...
x = 2^n → x & (x-1) = 0 (最高位 1 被消除,其餘本來就 0)
x ≠ 2^n → x & (x-1) ≠ 0 (至少剩一個 1)
int is_power_of_two(int x) {
// 必須 x > 0 排除 0 和負數 (負數最高位 1,但非 2 的冪)
return (x > 0) && ((x & (x - 1)) == 0);
}
/* 運算符優先級陷阱 */
if (x & (x-1) == 0) // ❌ 錯誤:== 優先級高於 &,解析為 x & ((x-1)==0)
if ((x & (x-1)) == 0) // ✅ 正確:顯式括號
is_power_of_two(8) → 1 (1000)
is_power_of_two(7) → 0 (0111)
is_power_of_two(0) → 0 (特殊處理)
is_power_of_two(-16) → 0 (負數非 2 的冪)
is_power_of_two(1) → 1 (2^0)
| 場景 | 位運算技巧 | 代碼片段 |
|---|---|---|
| 清零最低位 1 | `x &= (x-1)` | 核心原語 |
| 隔離最低位 1 | `x & (-x)` | 利用補碼:-x = ~x + 1 |
| 翻轉最低位 0 為 1 | `x | (x+1)` | 對偶操作 |
| 判斷異或同號 | `(x^y) >= 0` | 符號位異或判斷 |
| 無符號除以 2^n | `x >> n` | 邏輯右移 |
| 取模 2^n | `x & ((1<| 位掩碼 | |