位運算在算法題中的使用(C++)

一、算術運算符和位運算符
算術運算符:+、-、*、/、%
位運算符:&按位與、|按位或、^按位異或、~按位取反、<<左移右側補0、 >>右移左側補符號位、 >>>右移左側補0
注意:移位運算一定要賦值。也就是說將a左移2位不應該是a<<2;。而應該是a = a<<2;或者a <<= 2;
二、布隆過濾器

網頁黑名單系統
垃圾郵件過濾系統
爬蟲的網址判斷重複系統
容忍一定程度的失誤率
對空間要求嚴格
三、位運算練習題
1、不使用任何額外存儲空間而交換兩個整數
解法1:

class Swap {
public:
    vector<int> getSwap(vector<int> num) {
        num[0] = num[1]-num[0];
        num[1] = num[1]-num[0];
        num[0] = num[0]+num[1];
        return num;
    }
};

解法2:
class Swap {
public:
    vector<int> getSwap(vector<int> num) {
        num[0] ^= num[1];
        num[1] ^= num[0];
        num[0] ^= num[1];
       returnnum;
    }
};

2、比較兩個數的大小,不使用任何比較語句
解法:不使用比較語句而判斷出大小,首先想到用1、0和a、b兩數的線性組合來判斷大小。然後分析返回a有哪些情況,把這種情況用一個變量來記錄,把返回b的情況用另一個變量來記錄。情況分析如下表(+表示非負):
a
b
a-b
返回值
+
+
+
a
-
-
+
a
+
+
-
b
-
-
-
b
+
-
+(溢出-)
a
-
+
-(溢出+)
b
①返回a有兩種情況:a、b同號且a-b非負;a、b異號且a非負。
②返回b也有兩種情況:a、b同號且a-b爲負;a、b異號且a爲負。

這樣做可以不直接判斷a-b的符號,因爲有可能溢出。

classCompare {
public:
     
    intgetSign(intnum) {
        return((num >> 31)&1)^1;
    }
     
    intgetMax(inta, intb) {
        intas = getSign(a);
        intbs = getSign(b);
        intcs = getSign(a-b);
         
        intdif = as^bs;
        intsame = dif^1;
        intreta = same*cs+dif*as;
        intretb = reta^1;
        returnreta*a+retb*b;
    }
};

3、一個數組中只有一個數出現了奇數次,其餘數都出現了偶數次,找出這個出現奇數次的數
要求:O(N)\O(1)
解法:把所有數進行按位異或運算,出現偶數次的按位異或後爲0,出現奇數次的按位異或後不變。
classOddAppearance {
public:
    intfindOdd(vector<int> A, intn) {
        intres = A[0];
        for(inti = 1;i < n;i++) {
            res ^= A[i];
        }
        returnres;
    }
};

4、一個數組中有兩個數出現了奇數次,其餘數都出現了偶數次,找出這兩個出現奇數次的數
解法:
①遍歷第一遍將所有數據異或,得到出現奇數次的兩個數的異或結果xor
xor中爲1的位,在兩個數中一定是不同的,一個數在該位爲1另一個數爲0,找到xor中爲1的位(假設爲第k位)
③再一次遍歷數組,將第k位爲1的數異或,最後得到的結果就是要找的其中一個數,用xor與這個數異或就得到另一個要找的數。
class OddAppearance {
public:
    vector<int> findOdds(vector<int> arr, int n) {
        int xor = 0;
        vector<int> result;
        for (int i = 0;i < n;i++) {
            xor ^= arr[i];
        }

        int temp = xor &(~xor 1);// 該運算可以找到xor中從右邊起第一個爲1的位,將xor取反可以使第一個1的右邊一位變爲1,加
                                    // 1可以使第一個1的右邊一位進位,從而使第一個1所在的位變爲1,而其他的位均沒有改變,再與 
                                    // xor按位與就可以保留下第一個1.
        int res1 = 0;
        for (int j = 0;j < n;j++) {
            if ((arr[j]&temp) != 0) { //先按位與、按位或、按位異或再進行判斷的語句,位運算一定要加括號
                res1 ^=arr[j];
            }
        }

        int res2 = xor ^res1;
        result.push_back(min(res1,res2));
        result.push_back(max(res1,res2));
        return result;
    }
};

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章