首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

位運算技巧:快速判斷 2 的冪次與計算 32 位整數中 1 的個數

     編輯:關於C

核心結論

對於無符號整數 x:

  • 判斷是否為 2 的冪次:(x & (x - 1)) == 0 且 x != 0。
  • 計算二進制中 1 的個數 (Hamming Weight):反覆執行 x &= (x - 1),每次消除最低位的 1,循環次數即為 1 的個數。

原理圖解

為什麼 x & (x-1) 能消除最低位 1?

x       = xxxx 1 0000  (最低位 1 後面全 0)
x - 1   = xxxx 0 1111  (借位:最低位 1 變 0,後面全變 1)
x & (x-1)= xxxx 0 0000  (最低位 1 被清零,高位不變)
  • 若 x 只有 **一個 1**(即 2 的冪次),則 x & (x-1) == 0。
  • 若 x 有 **多個 1**,結果非零,且最低位 1 被清除。

代碼實現

1. 判斷 2 的冪次(巨集/函數)

#define IS_POWER_OF_TWO(x)  ((x) != 0 && ((x) & ((x) - 1)) == 0)

// 或函數版
int isPowerOfTwo(unsigned int x) {
    return x && !(x & (x - 1));
}

// 測試
int main() {
    unsigned int m = 512;  // 2^9
    printf("%d\n", isPowerOfTwo(m));  // 1 (真)
    m = 513;
    printf("%d\n", isPowerOfTwo(m));  // 0 (假)
    return 0;
}

2. 計算 1 的個數 (Brian Kernighan 算法)

int countOnes(unsigned int x) {
    int count = 0;
    while (x) {
        x &= (x - 1);  // 清除最低位 1
        count++;
    }
    return count;
}

// 時間複雜度:O(k),k 為 1 的個數,最壞 O(32) = O(1)

對比:其他常見計數方法

C++20 / GCC/Clang 內建函數

// 編譯器內建,通常優化為單條指令 (popcnt)
int cnt = __builtin_popcount(x);        // unsigned int
int cnt = __builtin_popcountll(x);      // unsigned long long

// C++20 標準庫
#include 
int cnt = std::popcount(x);

應用場景

  • **位圖/集合操作**:快速判斷集合大小、檢查單一元素。
  • **記憶體分配器**:Buddy System 判斷塊大小是否為 2 的冪次。
  • **網路協議**:校驗和、掩碼長度計算。
  • **圖形學**:Morton Code (Z-order)、稀疏集合遍歷。
  • **面試必考**:LeetCode 191 "Number of 1 Bits"、231 "Power of Two"。

擴展:隔離/提取最低位 1

x & -x        // 只保留最低位 1 (利用二進制補碼特性)
x | (x - 1)   // 最低位 1 及其右側全置 1

結合 x & (x-1) 可構建更多位操作技巧(如求下一個排列組合)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
方法時間複雜度特點
查表法 (8-bit/16-bit 分段)O(1) 固定查表最快,需額外 256/65536 字節表
並行計數 (SWAR)O(log 32) = 5 步無分支,適合 SIMD/GPU
Brian Kernighan (x &= x-1)O(k)代碼極短,1 越少越快,無額外空間
逐位移位檢查O(32)最慢,教學用