位操作與使用位操作解題的基本思路

如何通過(guò)位運(yùn)算巧解編程題

概念

位運(yùn)算是一種針對(duì)于小于一個(gè)字節(jié)數(shù)據(jù)進(jìn)行的數(shù)學(xué)運(yùn)算。計(jì)算機(jī)編程中歉提,需要進(jìn)行位運(yùn)算的操作包括底層硬件控制态兴,錯(cuò)誤檢測(cè)與修正算法狠持,數(shù)據(jù)壓縮,加密算法瞻润,優(yōu)化等喘垂。對(duì)于大多數(shù)其他任務(wù)來(lái)講,現(xiàn)代編程語(yǔ)言使得程序員可以直接對(duì)高層次的抽象進(jìn)行操作而不用直接進(jìn)行位操作绍撞。在源碼中正勒,主要用到的位操作包括了:AND,OR傻铣,XOR章贞,NOT,和按位移。

由于位運(yùn)算中對(duì)位的處理是并行的矾柜,所以位操作在某些情況下可以省去某些操作中對(duì)于數(shù)據(jù)結(jié)構(gòu)的循環(huán)便利從而提高運(yùn)算效率阱驾。但同時(shí),位操作使得代碼變得難以理解與維護(hù)怪蔑。

基礎(chǔ)

位運(yùn)算中包括了一些不同的作用于位級(jí)別的操作里覆,這些操作非常快缆瓣,可以用于優(yōu)化時(shí)間復(fù)雜度喧枷。一些基本的位操作包括了以下一些:

NOT(~) : 按位取反返回一個(gè)數(shù)字的補(bǔ)數(shù),例如如果某一位是0,它會(huì)將其變成1隧甚,反之亦然车荔。

N = 5 = (101)2

~N = ~5 = ~(101)2 = (010)2 = 2

AND(&) : 按位與是一個(gè)作用于相同長(zhǎng)度位模式的二元操作符。只在參與比較的兩個(gè)位同為1時(shí)戚扳,返回1忧便,否則返回0。

A = 5 = (101)2 , B = 3 = (011)2

A & B = (101)2 & (011)2= (001)2 = 1

OR(|) : 按位或同樣也是作用于兩個(gè)相同位長(zhǎng)度數(shù)字的二元操作符帽借,當(dāng)參與比較的兩個(gè)位其中有一個(gè)為1時(shí)珠增,返回1,當(dāng)兩個(gè)位都是0時(shí)砍艾,返回0蒂教。

A = 5 = (101)2 , B = 3 = (011)2

A | B = (101)2 | (011)2 = (111)2 = 7

XOR(^) : 異或同為二元操作符,當(dāng)兩個(gè)參與比較的位不同時(shí)返回1脆荷,否則返回0凝垛。

A = 5 = (101)2 , B = 3 = (011)2

A ^ B = (101)2 ^ (011)2 = (110)2 = 6

Left Shift (<<) : 左移操作符是一個(gè)二元操作符,其將操作數(shù)字按位左移一定位數(shù)并在末尾補(bǔ)0蜓谋。左移操作相當(dāng)于操作數(shù)與2k相乘(k為左移位數(shù))梦皮。

1 << 1 = 2 = 21

1 << 2 = 4 = 22

1 << 3 = 8 = 23

1 << n = 2n

Right Shift (>>) : 右移操作符同為二元操作符,其將作用數(shù)字按位向右移動(dòng)桃焕,并在末端補(bǔ)首位補(bǔ)0届氢。右移操作相當(dāng)于操作數(shù)與2k相除(k為右移位數(shù))。

4 >> 1 = 2

6 >> 1 = 3

5 >> 1 = 2

16 >> 4 = 1

X Y X&Y X/ Y X^Y
0 0 0 0 0 1
0 1 0 1 1 1
1 0 0 1 1 0
1 1 1 1 0 0

應(yīng)用示例

  1. 計(jì)算一個(gè)數(shù)字的二進(jìn)制表示中有多少個(gè)一

    //每一次n&(n-1)操作  都從末尾向前去掉一個(gè)1  直到數(shù)字減少為0
      int count_one(int n) {
        while(n) {
            n = n&(n-1);
            count++;
        }
        return count;
      }
    
  2. 判斷是否是為4次冪

    //!(n&(n-1))確定二進(jìn)制中只有一個(gè)1覆旭,n&0x55555555確定1的位置
    bool isPowerOfFour(int n) {
        return !(n&(n-1)) && (n&0x55555555);
        //check the 1-bit location;
    }
    
  3. 使用^與&運(yùn)算將兩個(gè)數(shù)字相加

    // 通過(guò)a^b得到?jīng)]有進(jìn)位的結(jié)果,通過(guò)a&b得到進(jìn)位岖妄,遞歸相加
    int getSum(int a, int b) {
        return b==0? a:getSum(a^b, (a&b)<<1); //be careful about the       terminating condition;
    }
    
  4. 從一個(gè)連續(xù)數(shù)組0,1,2,3,...,n中型将,找出缺少的一個(gè)數(shù)字,例如num=[0,1,3],則返回2荐虐。

    //由于相同的數(shù)字會(huì)因?yàn)閊抵消七兜,所以ret最后返回的是缺少數(shù)字與數(shù)組中最大數(shù)的^,這時(shí)候在與數(shù)組中最大數(shù)做^福扬,很自然的得到缺少的數(shù)字腕铸。
    int missingNumber(vector<int>& nums) {
        int ret = 0;
        for(int i = 0; i < nums.size(); ++i) {
            ret ^= i;
            ret ^= nums[i];
        }
        return ret^=nums.size();
    }
    
  5. 找到最接近給定數(shù)字N的2的n詞冪

    //實(shí)際就是定位數(shù)字最大端1的位置,將所有位置1后加一右移铛碑,即得到答案
    long largest_power(long N) {
        //changing all right side bits to 1.
        N = N | (N>>1);
        N = N | (N>>2);
        N = N | (N>>4);
        N = N | (N>>8);
        N = N | (N>>16);
        return (N+1)>>1;
    }
    

    N | (N>>1)后

    N | (N>>4) 后

    N | (N>>4) 后

    N | (N>>8)后

  6. 將一個(gè)無(wú)符號(hào)32位數(shù)字的所有位取反

    //從左到右遍歷N并從右到左置res中的1狠裹,或者相反方向。
    uint32_t reverseBits(uint32_t n) {
        unsigned int mask = 1<<31, res = 0;
        for(int i = 0; i < 32; ++i) {
            if(n & 1) res |= mask;
            mask >>= 1;
            n >>= 1;
        }
        return res;
    }
    
    uint32_t reverseBits(uint32_t n) {
       uint32_t mask = 1, ret = 0;
       for(int i = 0; i < 32; ++i){
          ret <<= 1;
          if(mask & n) ret |= 1;
             mask <<= 1;
           }
       return ret;
    }
    
    x = ((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1);
    x = ((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2);
    x = ((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4);
    x = ((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8);
    x = ((x & 0xffff0000) >> 16) | ((x & 0x0000ffff) << 16);
    
  7. 給定范圍[m,n],且0<=m<=n<=2147483647,返回范圍內(nèi)所有整數(shù)按位與的結(jié)果汽烦,如給定范圍[5,7]涛菠,則返回4。

    int rangeBitwiseAnd(int m, int n) {
       int a = 0;
       while(m != n) {
          m >>= 1;
          n >>= 1;
          a++;
       }
       return m<<a; 
    }
    
  8. 驗(yàn)證一個(gè)給定數(shù)字是否是2的n次方

    //2的n次方的二進(jìn)制表達(dá)中只會(huì)有1個(gè)1,而-1操作會(huì)將從左向右遇到的第一個(gè)變成0俗冻,而這一位之前的數(shù)字變成1
    //所以如果X只有一個(gè)1礁叔,那么-1后,除了1的那一位迄薄,其他都會(huì)變?yōu)?琅关,這樣兩個(gè)數(shù)字求與,應(yīng)該得到0.
    bool isPowerOfTwo(int x)
    {
       // x will check if x == 0 and !(x & (x - 1)) will check if x is a power of 2 or not
       return (x && !(x & (x - 1)));
    }
    
  9. 寫(xiě)一個(gè)程序返回?zé)o符號(hào)數(shù)里有多少個(gè)1

    //參照上一題讥蔽,只要N&(N-1)仍然大于0涣易,即可知,N中仍然有1存在勤篮。
    int hammingWeight(uint32_t n) {
       int count = 0;
       while(n) {
          n = n&(n-1);
          count++;
       }
       return count;
    }
    
    int hammingWeight(uint32_t n) {
       ulong mask = 1;
       int count = 0;
       for(int i = 0; i < 32; ++i){ //31 will not do, delicate;
          if(mask & n) count++;
          mask <<= 1;
       }
       return count;
    }
    
  10. 如果得到一個(gè)集合的所有子集
    一個(gè)大小為N的集合都毒,有2n個(gè)可能的子集,如果把每一個(gè)元素當(dāng)做一個(gè)位碰缔,2N的二進(jìn)制表達(dá)正好有N位账劲,把某一位值為1當(dāng)做存在,值為0時(shí)當(dāng)做不存在金抡,以此正好可以通過(guò)二進(jìn)制表達(dá)來(lái)確定每一個(gè)子集的元素瀑焦。

possibleSubsets(A, N):
   for i = 0 to 2^N:
      for j = 0 to N:
         if jth bit is set in i:
            print A[j]
                print ‘\n’
  1. 返回X二進(jìn)制表示中最靠右的一個(gè)1
//x-1 將最靠右的一個(gè)1置0,并將從這個(gè)1一直到最右的位置1梗肝,與x進(jìn)行與操作后榛瓮,實(shí)際上是將最右的一個(gè)1置0,與原數(shù)字異或操作后巫击,得到這個(gè)1禀晓。
int rightMostOne(int x){
   return x^x&(x-1);
}

// -x = ~x + 1 所以 -x 除了最右的一個(gè)1坝锰,和這個(gè)1往右的所有數(shù)字與原數(shù)相等粹懒,
int rightMostOne(int x){
   return x&(-x);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市顷级,隨后出現(xiàn)的幾起案子凫乖,更是在濱河造成了極大的恐慌,老刑警劉巖弓颈,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帽芽,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡翔冀,警方通過(guò)查閱死者的電腦和手機(jī)导街,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)纤子,“玉大人菊匿,你說(shuō)我怎么就攤上這事付呕。” “怎么了跌捆?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵徽职,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我佩厚,道長(zhǎng)姆钉,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任抄瓦,我火速辦了婚禮潮瓶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钙姊。我一直安慰自己毯辅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布煞额。 她就那樣靜靜地躺著思恐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪膊毁。 梳的紋絲不亂的頭發(fā)上胀莹,一...
    開(kāi)封第一講書(shū)人閱讀 52,696評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音婚温,去河邊找鬼描焰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛栅螟,可吹牛的內(nèi)容都是我干的荆秦。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼力图,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼萄凤!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起搪哪,我...
    開(kāi)封第一講書(shū)人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎坪圾,沒(méi)想到半個(gè)月后晓折,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡兽泄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年漓概,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片病梢。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡胃珍,死狀恐怖梁肿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情觅彰,我是刑警寧澤吩蔑,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站填抬,受9級(jí)特大地震影響烛芬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜飒责,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一赘娄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宏蛉,春花似錦遣臼、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至辟灰,卻和暖如春个榕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背芥喇。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工西采, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人继控。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓械馆,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親武通。 傳聞我的和親對(duì)象是個(gè)殘疾皇子霹崎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容