日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

位运算全面总结,关于位运算看这篇就够了

發布時間:2023/12/14 编程问答 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 位运算全面总结,关于位运算看这篇就够了 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 1 位運算概述
  • 2 位運算的性質
    • 2.1 運算符的優先級
    • 2.2 位運算符的運算律
  • 3 位運算高級操作
  • 4 負數的位運算
  • 5 位運算的一些應用
  • 6 位運算例題
    • 6.1 更新二進制位
    • 6.2 A+B問題
    • 6.3 O(1)時間檢測2的冪次

1 位運算概述

我們知道,計算機中的數在內存中都是以二進制形式進行存儲的 ,而位運算就是直接對整數在內存中的二進制位進行操作,因此其執行效率非常高,在程序中盡量使用位運算進行操作,這會大大提高程序的性能。

那么,涉及位運算的運算符如下表所示:

符號描述運算規則實例(以四位二進制數為例)
&兩個位都為1時,結果才為1。0001&0001=1,0001&0000=0,0000&0000=00000001\&0001=1,0001\&0000=0,0000\&0000=00000001&0001=1,0001&0000=0,0000&0000=0000
|兩個位都為0時,結果才為0。0001|0001=0001,0001|0000=0001,0000|0000=0000
^異或兩個位相同為0,相異為1。0001∧0001=0000,0001∧0000=1,0000∧0000=00001 \wedge0001=0000,0001\wedge0000=1,0000\wedge 0000=000010001=0000,00010000=1,00000000=0
~取反0變1,1變0。~0=1,~1=0\sim0=1,\sim 1 = 00=1,1=0
<<左移各二進位全部左移若干位,高位丟棄,低位補0。0001<<k=0100,k=20001<<k=0100,k=20001<<k=0100k=2kkk是左移的位數,這里k=2k=2k=2
>>右移各二進位全部右移若干位,對無符號數,高位補0,有符號數,右移補1110100>>k=0001,k=20100>>k=0001,k=20100>>k=0001k=2kkk是右移的位數,這里k=2k=2k=2

看完,你可能會覺得挺簡單的,但位運算的難點并不在這,而在于其性質、高級操作和它的應用。、

2 位運算的性質

2.1 運算符的優先級

優先級需要弄清楚,如果不太清楚可以加小括號確保是想要的運算順序,這里只是相對優先級,即只是和一些常用的算術運算符做比較。

優先級運算符結合方向
1?(符號運算符),~(取反運算符),++(自增),??(自減)-(符號運算符),\sim(取反運算符), ++(自增),--(自減)?(符號運算符),(取反運算符),++(自增),??(自減)從右到左
2?(乘),/(除),%(取余)*(乘),/(除),\%(取余)?(乘),/(除),%(取余)從左到右
3+(加),?(減)+(加),-(減)+(加),?(減)從左到右
4<<(左移),>>(右移)<<(左移),>>(右移)<<(左移),>>(右移)從左到右
5>(大于),<(小于),>=(大于等于),<=(小于等于)>(大于),<(小于),>=(大于等于),<=(小于等于)>(大于),<(小于),>=(大于等于),<=(小于等于)從左到右
6==(等于),!=(不等于)==(等于),!=(不等于)==(等于),!=(不等于)從左到右
7&(按位與)\&(按位與)&(按位與)從左到右
8∧(按位異或)\wedge (按位異或)(按位異或)從左到右
9|(按位或)從左到右

2.2 位運算符的運算律

公式名稱運算規則
交換律A&B=B&A ,A&B=B&A,A∧\wedge B=B∧\wedge A
結合律(注意結合律只能在同符號下進行)(A&B)&C=A&(B&C)(A\&B)\&C=A\&(B\&C)(A&B)&C=A&(B&C)$(A
等冪律A&A=AA\&A=AA&A=A,A|A=A
零律A&0=0A\&0=0A&0=0
互補律(注意,這不同于邏輯運算)A&~A=0A\&\sim A=0A&A=0,A|~A=?1\sim A=-1A=?1
同一律A|0=A,A∧0=A\wedge 0 =A0=A

以上僅為已證明的運算律(可能存在遺漏),其余的博主均認為是不符合不成立的,注意:千萬不要將邏輯運算的運算律或者其他的運算律與這混為一談。

3 位運算高級操作

如下表,請讀者認真閱讀理解,在閱讀的過程中可以對示例進行運算。

功能示例位運算
去掉最后一位0100?>00100100->00100100?>0010x>>1x>>1x>>1
在最后加一個0000100?>10000100->10000100?>1000x<<1x<<1x<<1
在最后加一個10100?>10010100->10010100?>1001x<<1+1x<<1+1x<<1+1
將最后一位變為1110100?>01010100->01010100?>0101x|1
將最后一位變為0000101?>01000101->01000101?>0100,這里實際上就是先確保最低位變為111,再減去111(x|1)-1
最后一位取反0100?>01010100->01010100?>0101 ,利用異或性質,其中除最后一位其余不變。x∧1x\wedge1x1
把右數的第kkk位變為1110001?>1001,k=40001->1001,k=40001?>1001,k=4x|(1<<(k-1))
把右數的第kkk位變為0001001?>0001,k=41001->0001,k=41001?>0001,k=4,這個操作實際上就是先得到了100010001000,然后取反得到011101110111,最后利用按位與的性質其余位不變,最高位為000x&~(1<<(k?1))x\&\sim(1<<(k-1))x&(1<<(k?1))
把右數的第kkk位取反1000?>0000,k=41000->0000,k=41000?>0000,k=4,利用異或性質x∧(1<<(k?1))x\wedge (1<<(k-1))x(1<<(k?1))

由于表長限制,這里接下表繼續:

功能示例位運算
取末kkk1011?>0011,k=21011->0011,k=21011?>0011,k=2x&(1<<k?1)x\&(1<<k-1)x&(1<<k?1)
取右數的第kkk1011?>0001,k=41011->0001,k=41011?>0001,k=4,右移k?1k-1k?1位則是去掉了最后的k?1k-1k?1位,我們利用按位與即可將其提取出來x>>(k?1)&1x>>(k-1)\&1x>>(k?1)&1
把末kkk位全變為1111000?>1111,k=31000->1111,k=31000?>1111,k=3x|(1<<k-1)
把末kkk位取反0101?>1010,k=40101->1010,k=40101?>1010,k=4x∧(1<<k?1)x\wedge (1<<k-1)x(1<<k?1)
把右邊連續的111變為0000111?>00000111->00000111?>0000 ,注意是右起連續的111x&(x+1)x\&(x+1)x&(x+1)
把右起的第一個000變為1110011?>01110011->01110011?>0111x|(x+1)
把右起連續的000變為1111000?>11111000->11111000?>1111,注意是右起連續的000x|(x-1)
取右邊連續的1111011?>00111011->00111011?>0011(x∧(x+1))>>1(x\wedge (x+1))>>1(x(x+1))>>1
去掉右起的第一個111的左邊1101?>00011101->00011101?>0001x&(x∧(x?1))x\&(x\wedge (x-1))x&(x(x?1))

當然,這里只是一些常用的,并不是全部,位運算的神奇遠不止于此。

4 負數的位運算

首先,我們要知道,在計算機中,運算是使用的二進制補碼,而正數的補碼是它本身,負數的補碼則是符號位不變,其余按位取反,最后再+1+1+1得到的, 例如:

151515,原碼:0000111100001111\space00001111?補碼:000011110000111100001111

?15-15?15,原碼:1000111110001111\space10001111?補碼:111100011111000111110001

那么對于負數的位運算而言,它們的操作都是建立在補碼上的,得到的運算結果是補碼,最后將補碼結果轉化成一個普通的十進制數結果。但需要注意的是,符號位是需要參與運算的,而在左移右移操作中,負數右移補111,左移右邊補000。例如對于?15-15?15,其補碼為11110001,11110001,11110001,右移一位(?15>>1)(-15>>1)(?15>>1)得到的是111110001111100011111000,即?8-8?8,其他的同理。

這里我們介紹幾個特殊的性質:

  • 快速判斷是否為?1-1?1

    在鏈式前向星中,我們初始化headheadhead數組為?1-1?1,最后判斷是否遍歷完uuu的所有邊時,即判斷iii是否為?1-1?1,我們直接用~i\sim ii即可。原因就在于?1-1?1的補碼是111111111111111111111111,按位取反就變為000000000000000000000000,這實際上就是000

  • 取最低位的111,lowbit函數

    也就是x&(?x)x\&(-x)x&(?x),這在樹狀數組中起著巨大作用,這里指路一篇樹狀數組講解blogblogblog:點這里,我們來證明一下,這里取x=15x=15x=15,對于15&(?15)15\&(-15)15&(?15),我們知道,在補碼上進行運算得到的是000000010000000100000001,需要注意二元運算的符號位我們需要進行運算。

5 位運算的一些應用

  • 位運算實現乘除法

    xxx左移一位實現×2\times 2×2,將xxx右移一位實現÷2\div2÷2

    a<<1≡a?2a<<1 \equiv a*2a<<1a?2

    a>>1≡a/2a >>1 \equiv a/2a>>1a/2

  • 位運算交換兩整數

void swap(int &a,int &b){a ^= b;b ^= a;a ^= b;}

這效率非常高,我們來剖析其原理,對于a=a∧ba=a\wedge ba=ab,則b=b∧(a∧b)b = b\wedge(a\wedge b)b=b(ab),根據交換律以及異或性質,得b=b∧b∧a=0∧a=ab=b\wedge b\wedge a=0\wedge a=ab=bba=0a=a,同理a=(a∧b)∧a=0∧b=ba=(a\wedge b)\wedge a=0\wedge b=ba=(ab)a=0b=b。這樣就實現了交換操作。

  • 位運算判斷奇偶數

    我們知道,在二進制中,最低位決定了是奇數還是偶數,所以我們可以提取出最低位的值,即與111相與即可實現目的,為000則是偶數,為111則是奇數。

  • 位運算改變正負性和求絕對值

int change(int a){return ~ a + 1; }

對于正數而言,補碼就是原碼,所以按位取反再+1+1+1則得到對應真值負數的補碼,而對于負數,其補碼進行按位取反再+1+1+1則得到對應真值正數的補碼,變為原碼。那么知道這個我們就可以特判是否為負數==(這里通過右移313131位,若為正數,則得到的是000,若為負數,則得到的是?1-1?1,而000的補碼為000000000000,?1-1?1的補碼為111111111111,根據異或性質即可判斷。)== 再進行反轉即可實現求絕對值了。如下:

int abs(int a){return a ^ (a >> 31) ? a : ~ a + 1; }
  • 位運算實現對ppp取余(p為2k2^k2k
int mod(int a,int p){return a & (p - 1); }

取余實際上就是舍去大于等于ppp的位數,所以我們只需要保留在ppp范圍內的數。由于我們限定了ppp2k2^k2k,所以(p?1)(p - 1)(p?1)一定是將小于ppp的最高位全部變為了111,這樣再進行與操作即可得到余數。

  • 位運算統計二進制數111的個數
int count(int x){int cnt = 0;while(x){x = x & (x - 1);cnt ++;}return cnt; }

對于任意的x,轉換成二進制后,是形如這樣的數字:aa…aa10…00,從右向左數有任意多個0,直到遇見第一個1,字母a用來占位,代表1左邊的任意數字。x-1轉換成二進制后,是形如這樣的數字:aa…aa01…11,從右向左數,原來的任意多個0都變成1,原來的第一個1,變成0,字母a部分不變。對x 和 x-1 進行 按位與 計算,會得到:aa…aa00…00,從右向左數,原來的第一個1變成了0,字母a部分不變。所以 x & (x-1)相當于消除了 x 從右向左數遇到的第一個1。那么,x轉換成二進制后包含多少個1,count函數里的循環就會進行多少次,直到x所有的1都被“消除”。

6 位運算例題

6.1 更新二進制位

  • 題面

    給出兩個32位的整數N和M,以及兩個二進制位的位置i和j。寫一個方法來使得N中的第i到j位等于M(M會是N中從第i為開始到第j位的子串)

    樣例:

    輸入: N=(10000000000)2 M=(10101)2 i=2 j=6 輸出: N=(10001010100)2 輸入: N=(10000000000)2 M=(11111)2 i=2 j=6 輸出: N=(10001111100)2
  • 解題思路

    結合所學,我們的思路應該就是先將第iii位到第jjj位全部變為000,再將與左移iii位的MMM進行或操作。

  • AC代碼

class Solution { public:int updateBits(int n, int m, int i, int j) {for(int pos = i; pos <= j; pos ++ ){n &= ~(1 << pos);}return n | (m << i);} };

6.2 A+B問題

  • 題面

    給出兩個整數 a 和 b , 求他們的和并以整數(int)的形式返回。不能使用 + 等數學運算符。

    樣例:

    輸入:

    a = 1 b = 2

    輸出:

    3

    輸入:

    a = -1 b = 1

    輸出:

    0
  • 解題思路

    這題我們可以利用異或操作來實現,因為異或操作有一個別名叫不進位加法。那么進位操作我們實際上就可以通過a&ba\&ba&b來實現,因為a&ba\&ba&b得到的都是aaabbb上都有的111,我們再左移即得到的是進位之后的結果,所以a+b=(a∧b)+(a&b<<1)a+b=(a\wedge b)+(a\&b<<1)a+b=(ab)+(a&b<<1)。通過這樣模擬豎式加法操作即可。

  • AC代碼

class Solution { public:int aplusb(int a, int b) {while(b != 0){int temp_a = a ^ b;int temp_b = (a & b) << 1;a = temp_a;b = temp_b;}return a;} };

6.3 O(1)時間檢測2的冪次

  • 題面

    用 O(1) 時間檢測整數 n 是否是 2 的冪次。

    樣例

    n=4,返回 true;

    n=5,返回 false.

    挑戰

    O(1) 時間復雜度

  • 解題思路

    首先我們知道2k2^k2k是大于000的,這里我們需要特判,同理,2k2^k2k的二進制表示中只有111111,故我們可以利用x&(x?1)x\&(x-1)x&(x?1)來消除唯一的111判斷是否等于000即可。

  • AC代碼

class Solution { public:bool checkPowerOf2(int n) {// write your code herereturn n > 0 && (n & (n - 1)) == 0;} };

總結

以上是生活随笔為你收集整理的位运算全面总结,关于位运算看这篇就够了的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。