Bit Manipulation常用總結

1.判斷奇偶

奇偶數的特徵:如果一個數是偶數,則對於其二進制來說,最低位肯定是0;如果一個數是奇數,則對於其二進制來說,最低位肯定是1,因爲在二進制表示中,只有 2^{_{0}}會產生1

所以可以利用這一特徵來判斷奇偶數

if((num&1) == 0)
{
    //偶數
}
else
{
    //奇數
}

注意:不用寫爲  num&1 == 0 ,這樣的判斷是錯誤的,必須給前面帶上括號;因爲 == 優先級高於 位運算符&

2.消去一個數中的最低位

比如十進制數5(0101),  我們要消去其二進制中的最低位的1

則 操作是 (5&4) = (0101 & 0100) = 0100,從而將最低位處的1消除

典型應用:統計一個數的二進制表示中有多少個1

    int hammingWeight(uint32_t n) 
    {
     
        int count = 0;
        while(n)
        {
            count++;
            n &= (n-1);
        }

        return count;
    }

3.將二進制數進行翻轉操作

可以用到C++中的bitset,它是類似於一個數組的結構,其內部元素只能是0或1,且每一位只佔1bit

bitset數組簡介:

bitset<8> bitset1; //構造長度爲4的bitset數組,每一位默認爲0
bitset<8> bitset2(12); //構造長度爲8的數組,並用12的二進制去初始化
//也可以用字符串來進行構造
string s = "11000";
bitset<8> bitset3(s);

//注:當bitset的長度大於初始化所用的二進制數長度時,則在前面用0補足
//而當初始化所用的二進制長度大於bitset長度時,對於整數則只取整數後面部分,對於字符串則只取字符串前面部分

//bitset數組支持類似數組的[]操作,可以看做數組進行處理
//bitset支持位運算的所有操作符


//bitset的屬性
bitset<8> foo ("10011011");

    cout << foo.count() << endl;  //5  (count函數用來求bitset中1的位數,foo中共有5個1
    cout << foo.size() << endl;   //8  (size函數用來求bitset的大小,一共有8位

    cout << foo.any() << endl;  //true  (any函數檢查bitset中是否有1
    cout << foo.none() << endl;  //false  (none函數檢查bitset中是否沒有1
    cout << foo.all() << endl;  //false  (all函數檢查bitset中是全部爲1

//bitset的類型轉換函數
bitset<8> foo ("10011011");

    string s = foo.to_string();  //將bitset轉換成string類型
    unsigned long a = foo.to_ulong();  //將bitset轉換成unsigned long類型
    unsigned long long b = foo.to_ullong();  //將bitset轉換成unsigned long long類型

    cout << s << endl;  //10011011
    cout << a << endl;  //155
    cout << b << endl;  //155

所有,進行二進制的反轉就和數組的反轉操作是一樣的

    uint32_t reverseBits(uint32_t n) 
    {

        bitset<32> bit_set(n);
        int i = 0; 
        int j = 31;
        while(i < j)
        {
            bool tmp = bit_set[i];
            bit_set[i++] = bit_set[j];
            bit_set[j--] = tmp;
        }
        return (uint32_t) bit_set.to_ulong();
    }

4.不借助外部變量實現兩個數的交換

常規的實現兩個數的交換通常藉助外部變量來實現,比如

int a = 1;
int b = 2;
//實現兩個數的交換,藉助外部變量c
int c = a; //c = 1
a = b;     // a = 2
b = c;    // b = 1
不借助外部變量可以通過異或來實現
int a = 1;
int b = 2;

a ^= b; // a = 1^2 = 3
b ^= a; // b = 2 ^ 3 = 1
a ^= b; // a = 3 ^ 1 = 2
//所以最後 a = 2; b = 1;實現了兩者的交換

5.求一個數的相反數

由於補碼原理,對一個數進行取反後+1就可以得到它的相反數

6.求一個數的絕對值

由於正數的絕對值是其本身,負數的絕對值是其相反數,所以只需要判定正負,然後進行操作即可

如何判斷正負呢?

對於32位整數來言,最高位是符合位,1代表負數,0代表正數

int myabs(int num)
{
    int  flag = num >> 31;
    return flag == 0 ? num : ~num+1;
}

在此基礎上可以進一步優化,由於 一個數與0異或相當於其本身,而與-1異或相當於 對本身進行取反操作,所以

int myabs(int num)
{
    int  flag = num >> 31;
    return (flag^num) - flag;//如果flag = 0,則返回 num本身,若flag=-1,則返回 ~num+1
}

7.通過位運算來判斷兩個小寫單詞(字符串)是否有相同字符

由於只包含小寫字母,所以總共有26位,而int有32位,所以我們可以用int的低26位來進行表示這26個字母

比如"abc", 用位來表示可以表示爲 0111,然後由於每一位均代表一個字符,所以我們最後讓兩個位表示的字符串進行相與即可,如果結果爲1,則表示兩個字符串存在相同字符,否則兩者不存在相同字符

string s1 = "abc";

string s2 = "cde";

bool commonstr(string s1, string s2)
{
    vector<int> mask(2,0);
    for(char c: s1)
    {
        mask[0]  |= 1 << (c - 'a');
    }

    for(char c: s2)
    {
        mask[1]  |= 1 << (c - 'a');
    }

    if(mask[0]&mask[1])
    {

        return true;
    }

    return false;

}

 

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