筆試面試中常見的位運算用法


   本文是准備找工作過程中關於位運算的一些積累和記錄的整理。注意:部分位運算的處理結果依賴於變量所屬類型的字長,使用時請結合具體環境修改。

 

1.XOR應用

性質:滿足交換率、結合律,一個數與其自身異或結果為0。

(1)不用中間變量,交換兩數

a = a^b;
b = b^a; //b = b^(a^b),thus b becomes the earlier a
a = a^b; //a = (a^b)^a,thus a becomes the earlier b

 

擴展:不用異或,同樣也能不用中間變量,交換兩數

a = a - b;
b = a + b; // b = (a - b)+ b, thus b becomes the earlier a
a = b - a; // a = a - (a - b),  thus a becomes the earlier b

但是這種方式引入了一個陷阱,如果a是一個很大的正數而b是一個很小的負數,那么a-b就會溢出。雖然在b=a+b時可能會通過再一次溢出從而獲得真實的a的值,不推薦這種利用未定義行為的解法

如何理解這種解法?其實第一行是a=a-b還是a=a+b再或者是a=a*b都可以,對應地在第二行把b通過這個式子和b本身的運算求出a即可,再在第三行利用ab的組合值以及原先的a求解b。明顯地,使用*比+或-更容易溢出。理解后,完全不必死記硬背這三個式子,看成是解方程就不難了。

 

(2)尋找只出現1次的一個數,其他數出現偶數次(或尋找唯一一個出現奇數次的數,其他數出現偶數次)

解法:全部數做XOR,最后的結果就是要找的數。

 

擴展:尋找出現奇數次的數,其他不必尋找的數只出現偶數次。 

常見的面試題擴展,思路還是原來的思路,先全部XOR一遍,在獲得的結果上,對每一位為1(即可能有兩個不同的數,二進制標識中該位不同)進行分組,構造出所有待找出的數。

這么概括很抽象,看一道具體的筆試題吧,通過解題就容易理解了。

(小米2013校招筆試題)一個數組里,除了三個數是唯一出現的,其余的都出現偶數個,找出這三個數中的任一個。比如數組元素為【1, 2,4,5,6,4,2】,只有1,5,6這三個數字是唯一出現的,我們只需要輸出1,5,6中的一個就行。

解答:http://blog.csdn.net/leo115/article/details/8036990

 

(3)NIM游戲的狀態分析

  請參考《編程之美》1.12 NIM(2) “拈”游戲分析。其核心是,兩種完全不同的狀態(安全狀態和不安全狀態)的XOR值恰為0和1。

 

2.加法,不用+-*/做加法(《劍指Offer》面試題47

迭代版本(《劍指Offer》面試題47)

int Add(int num1,int num2)
{
    int sum,carry;
    do {
        sum = num1^num2;
        carry= (num1 & num2)<<1;
        num1 = sum;
        num2 = carry;
    } while (num2!=0)
    return num1;
}

 

遞歸版本(CareerCup 20.1)

int add_no_arithm(int num1,int num2)
{
    if(num2 == 0)
        return num1;
    int sum = a^ b;
    int carry = (a&b)<<1;
    return add_no_arithm(sum,carry);
}

 

3.求兩數的平均數 不用-、*、/求兩數的平均數 

  似乎是出自《程序員面試寶典》,但是我在第三版第12章沒找到原題。用下面的代碼就能“神奇地”獲得兩個整型的平均值

int  average(int x,int y)
{
    return ( (x&y) + ( (x^y)>>1 ) );
}

  解釋請看:http://blog.csdn.net/leo115/article/details/7993110,不過也是轉載,原出處疑似已失效。

   

4.不用*和/做除法(《算法設計手冊》面試題1-28)

  慢速版本和優化版本請參考舊作:http://www.cnblogs.com/wuyuegb2312/p/3257558.html

  縱觀第2、3、4條可以發現,如果問題限制不允許使用某種四則運算符以及%,就可以在位運算上打主意了。

 

5.二進制中1的個數

  不要覺得很trick,這是K&R提到過的。值得注意的是,如果使用C實現,為了避免實現定義不同造成的結果不同,需要把該變量轉化為無符號型。

int bitcount(unsigned x)
{
    int b;
    for(b=0;x|=0;x>>=1)
        if(x&01)
            b++;
    return b;
}

  事實上K&R習題2-9提到了一種更快的算法:

int bitcount(unsigned x)
{
    int b;
    for(b=0;x!=0;x&= x-1)
        b++;
    return b;
}

 

6.從無符號型x的第p位開始,取n位(K&R)

//最低位是第0位
unsigned getbits(unsigned x,int p, int n)
{
    return (x>>(p+1-n)) & ~(~0<<n);
}

 

7.利用同余的性質和位運算加速的輾轉相減求最大公約數法(《C語言參考手冊》第七章)

unsigned binary_gcd(unsigned x, unsigned y)
{
    unsigned temp;
    unsigned common_power_of_two = 0;
    if(x==0)
        return 0;
    if(y==0)
        return 0;

    /*find the largest power of two
    that divides both x and y*/
    while(((x|y)&1)==0) {
        x >>= 1;
        y >>= 1;
        ++common_power_of_two;
    }
    while((x &1) == 0)
        x >>= 1;
    while(y) {
        /*x is odd and y is nonzero here*/
        while((y&1)==0)
            y >>= 1;
        /*x and y are odd here*/
        temp = y;
        if (x>y)
            y = x - y;
        else
            y = y-x;
        x = temp;
        /*Now x has the old value of y,which is odd.
         y is even,because it is the difference of 
        two odd numbers therefore it will be right-shifted
        at least once on the next iteration.*/
    }
    return (x<<common_power_of_two);
}

 

8.不用大於小於號,求兩數較大值(CareerCup 19.4)

int getMax(int a,int b)
{
    int c = a - b;
    int k = (c>>31)&0x1;
    int max = a-k*c;
    return max;
}

   在不允許使用>、<以及任何判斷語句時,使用移位判斷一個數的正負性是常用技巧,如《深入理解計算機系統》2.3.7節習題2.42中求補碼對給定2的冪的商的解法。這個問題以及求解涉及到補碼的表示和運算的知識,在此不進行詳述,有興趣的讀者可以自行查閱。

  更多關於《深入理解計算機系統》習題答案的內容,可以參考:http://blog.csdn.net/yang_f_k

 

9.實現位向量

  這種做法是對空間的高效利用。對《編程珠璣》上位向量實現全面分析的舊作一篇:http://www.cnblogs.com/wuyuegb2312/p/3136831.html

 

10.其他

  附上MoreWindows前輩的一篇博文鏈接:位操作基礎篇之位操作全面總結,順便把該文的目錄拿來做個索引:

  1. 一 位操作基礎
  2. 二 常用位操作小技巧
    1. 判斷奇偶
    2. 交換兩數
    3. 變換符號
    4. 求絕對值
  3. 三 位操作與空間壓縮
  4. 四 位操作的趣味應用
    1.   高低位交換
    2.   二進制逆序
    3.   二進制中1的個數
    4.   缺失的數字


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM