位运算全面总结,关于位运算看这篇就够了
文章目錄
- 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=00001∧0001=0000,0001∧0000=1,0000∧0000=0 |
| ~ | 取反 | 0變1,1變0。 | ~0=1,~1=0\sim0=1,\sim 1 = 0~0=1,~1=0 |
| << | 左移 | 各二進位全部左移若干位,高位丟棄,低位補0。 | 0001<<k=0100,k=20001<<k=0100,k=20001<<k=0100,k=2,kkk是左移的位數,這里k=2k=2k=2 |
| >> | 右移 | 各二進位全部右移若干位,對無符號數,高位補0,有符號數,右移補111。 | 0100>>k=0001,k=20100>>k=0001,k=20100>>k=0001,k=2,kkk是右移的位數,這里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=-1~A=?1 |
| 同一律 | A|0=A,A∧0=A\wedge 0 =A∧0=A |
以上僅為已證明的運算律(可能存在遺漏),其余的博主均認為是不符合不成立的,注意:千萬不要將邏輯運算的運算律或者其他的運算律與這混為一談。
3 位運算高級操作
如下表,請讀者認真閱讀理解,在閱讀的過程中可以對示例進行運算。
| 去掉最后一位 | 0100?>00100100->00100100?>0010 | x>>1x>>1x>>1 |
| 在最后加一個000 | 0100?>10000100->10000100?>1000 | x<<1x<<1x<<1 |
| 在最后加一個1 | 0100?>10010100->10010100?>1001 | x<<1+1x<<1+1x<<1+1 |
| 將最后一位變為111 | 0100?>01010100->01010100?>0101 | x|1 |
| 將最后一位變為000 | 0101?>01000101->01000101?>0100,這里實際上就是先確保最低位變為111,再減去111。 | (x|1)-1 |
| 最后一位取反 | 0100?>01010100->01010100?>0101 ,利用異或性質,其中除最后一位其余不變。 | x∧1x\wedge1x∧1 |
| 把右數的第kkk位變為111 | 0001?>1001,k=40001->1001,k=40001?>1001,k=4 | x|(1<<(k-1)) |
| 把右數的第kkk位變為000 | 1001?>0001,k=41001->0001,k=41001?>0001,k=4,這個操作實際上就是先得到了100010001000,然后取反得到011101110111,最后利用按位與的性質其余位不變,最高位為000 | x&~(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)) |
由于表長限制,這里接下表繼續:
| 取末kkk位 | 1011?>0011,k=21011->0011,k=21011?>0011,k=2 | x&(1<<k?1)x\&(1<<k-1)x&(1<<k?1) |
| 取右數的第kkk位 | 1011?>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位全變為111 | 1000?>1111,k=31000->1111,k=31000?>1111,k=3 | x|(1<<k-1) |
| 把末kkk位取反 | 0101?>1010,k=40101->1010,k=40101?>1010,k=4 | x∧(1<<k?1)x\wedge (1<<k-1)x∧(1<<k?1) |
| 把右邊連續的111變為000 | 0111?>00000111->00000111?>0000 ,注意是右起連續的111 | x&(x+1)x\&(x+1)x&(x+1) |
| 把右起的第一個000變為111 | 0011?>01110011->01110011?>0111 | x|(x+1) |
| 把右起連續的000變為111 | 1000?>11111000->11111000?>1111,注意是右起連續的000 | x|(x-1) |
| 取右邊連續的111 | 1011?>00111011->00111011?>0011 | (x∧(x+1))>>1(x\wedge (x+1))>>1(x∧(x+1))>>1 |
| 去掉右起的第一個111的左邊 | 1101?>00011101->00011101?>0001 | x&(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 i~i即可。原因就在于?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<<1≡a?2
a>>1≡a/2a >>1 \equiv a/2a>>1≡a/2
-
位運算交換兩整數
這效率非常高,我們來剖析其原理,對于a=a∧ba=a\wedge ba=a∧b,則b=b∧(a∧b)b = b\wedge(a\wedge b)b=b∧(a∧b),根據交換律以及異或性質,得b=b∧b∧a=0∧a=ab=b\wedge b\wedge a=0\wedge a=ab=b∧b∧a=0∧a=a,同理a=(a∧b)∧a=0∧b=ba=(a\wedge b)\wedge a=0\wedge b=ba=(a∧b)∧a=0∧b=b。這樣就實現了交換操作。
-
位運算判斷奇偶數
我們知道,在二進制中,最低位決定了是奇數還是偶數,所以我們可以提取出最低位的值,即與111相與即可實現目的,為000則是偶數,為111則是奇數。
-
位運算改變正負性和求絕對值
對于正數而言,補碼就是原碼,所以按位取反再+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)
取余實際上就是舍去大于等于ppp的位數,所以我們只需要保留在ppp范圍內的數。由于我們限定了ppp為2k2^k2k,所以(p?1)(p - 1)(p?1)一定是將小于ppp的最高位全部變為了111,這樣再進行與操作即可得到余數。
- 位運算統計二進制數111的個數
對于任意的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代碼
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得到的都是aaa和bbb上都有的111,我們再左移即得到的是進位之后的結果,所以a+b=(a∧b)+(a&b<<1)a+b=(a\wedge b)+(a\&b<<1)a+b=(a∧b)+(a&b<<1)。通過這樣模擬豎式加法操作即可。
-
AC代碼
6.3 O(1)時間檢測2的冪次
-
題面
用 O(1) 時間檢測整數 n 是否是 2 的冪次。
樣例
n=4,返回 true;
n=5,返回 false.
挑戰
O(1) 時間復雜度
-
解題思路
首先我們知道2k2^k2k是大于000的,這里我們需要特判,同理,2k2^k2k的二進制表示中只有111個111,故我們可以利用x&(x?1)x\&(x-1)x&(x?1)來消除唯一的111判斷是否等于000即可。
-
AC代碼
總結
以上是生活随笔為你收集整理的位运算全面总结,关于位运算看这篇就够了的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 漏洞C:/Windows/Fonts/c
- 下一篇: 计算机考试关于计算量,2020年税务师考