位运算——强大得令人害怕
前言
眾所周知,位運算是我們學計算機必學的東西,前人用二進制、位運算給我們了一個操作簡單的計算機,但我們卻很少接觸位運算了。今天介紹一些位運算在算法中的運用。
位運算基礎
- &
- 按位與
- 如果兩個相應的二進制位都為1,則該位的結果值為1,否則為0
- |
- 按位或
- 兩個相應的二進制位中只要有一個為1,該位的結果值為1
- ^
- 按位異或
- 若參加運算的兩個二進制位值相同則為0,否則為1
- ~
- 取反
- ~是一元運算符,用來對一個二進制數按位取反,即將0變1,將1
- <<
- 左移
- 用來將一個數的各二進制位全部左移N位,右補0
- >>
- 右移
- 將一個數的各二進制位右移N位,移到右端的低位被舍棄,對于無符號數, 高位補0
奇技淫巧
1.技巧一:用于消去x的最后一位的1
x & (x-1) x = 1100 x-1 = 1011 x & (x-1) = 10001.1.應用一 用O(1)時間檢測整數n是否是2的冪次.
- 思路解析:N如果是2的冪次,則N滿足兩個條件。
1.N>0
2.N的二進制表示中只有一個1
一位N的二進制表示中只有一個1,所以使用N&(N-1)將唯一的一個1消去。
如果N是2的冪次,那么N&(N-1)得到結果為0,即可判斷。
1.2.應用二 計算在一個 32 位的整數的二進制表示中有多少個 1.
- 思路解析:
由 x & (x-1) 消去x最后一位知。循環使用x & (x-1)消去最后一位1,計算總共消去了多少次即可。
1.3.將整數A轉換為B,需要改變多少個bit位
- 思路解析
這個應用是上面一個應用的拓展。
思考將整數A轉換為B,如果A和B在第i(0<=i<32)個位上相等,則不需要改變這個BIT位,如果在第i位上不相等,則需要改變這個BIT位。所以問題轉化為了A和B有多少個BIT位不相同。聯想到位運算有一個異或操作,相同為0,相異為1,所以問題轉變成了計算A異或B之后這個數中1的個數。
2.技巧二 使用二進制進行子集枚舉
應用.給定一個含不同整數的集合,返回其所有的子集
樣例
如果 S = [1,2,3],有如下的解:
[ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2] ]
思路
思路就是使用一個正整數二進制表示的第i位是1還是0,代表集合的第i個數取或者不取。所以從0到2n-1總共2n個整數,正好對應集合的2^n個子集。
S = {1,2,3} N bit Combination 0 000 {} 1 001 {1} 2 010 {2} 3 011 {1,2} 4 100 {3} 5 101 {1,3} 6 110 {2,3} 7 111 {1,2,3}解題代碼
之后補充。
技巧三.a^b^b=a
3.1.應用一 數組中,只有一個數出現一次,剩下都出現三次,找出出現一次的。
問題
Given [1,2,2,1,3,4,3], return 4
解題思路
因為只有一個數恰好出現一個,剩下的都出現過兩次,所以只要將所有的數異或起來,就可以得到唯一的那個數。
C語言解題代碼
#include<stdio.h> int main() {int a[7]={1,2,2,1,3,4,3};int ans=0;for(int i=0;i<7;i++){ans^=a[i];}printf("%d\n",ans); }3.2.應用二 數組中,只有一個數出現一次,剩下都出現三次,找出出現一次的。
問題
Given [1,1,2,3,3,3,2,2,4,1] return 4
解題思路
因為數是出現三次的,也就是說,對于每一個二進制位,如果只出現一次的數在該二進制位為1,那么這個二進制位在全部數字中出現次數無法被3整除。
模3運算只有三種狀態:00,01,10,因此我們可以使用兩個位來表示當前位%3,對于每一位,我們讓Two,One表示當前位的狀態,B表示輸入數字的對應位,Two+和One+表示輸出狀態。
C語言解題代碼
#include<stdio.h>void findNum(int *a,int n) {int one=0;int two=0;int i,j,k;for(i=0;i<n;i++){two=two|(one&a[i]);one=one^a[i];int three=two&one;two=two^three;one=one^three;}printf("%d\n",one|two); } int main() {int a[10]={1,1,2,3,3,3,2,2,4,1};findNum(a,10); }另外一種容易理解的方法
#include<stdio.h>void findNum(int *a,int n) {int ans=0;int bits[32]={0};for(int i=0;i<n;i++){for(int j=0;j<32;j++){bits[j]+=((a[i]>>j)&1);}}for(int i=0;i<32;i++){if(bits[i]%3==1) ans+=1<<i;}printf("%d\n",ans); } int main() {int a[10]={1,1,2,3,3,3,2,2,4,1};findNum(a,10); }3.3.應用三 數組中,只有兩個數出現一次,剩下都出現兩次,找出出現一次的
問題
Given [1,2,2,3,4,4,5,3] return 1 and 5
解題思路
有了第一題的基本的思路,我們不妨假設出現一個的兩個元素是x,y,那么最終所有的元素異或的結果就是res = x^y。并且res!=0,那么我們可以找出res二進制表示中的某一位是1,那么這一位1對于這兩個數x,y只有一個數的該位置是1。對于原來的數組,我們可以根據這個位置是不是1就可以將數組分成兩個部分。求出x,y其中一個,我們就能求出兩個了。
C語言解題代碼
#include<stdio.h>void findNum(int *a,int n) {int ans=0;int pos=0;int x=0,y=0;for(int i=0;i<n;i++)ans^=a[i];int tmp=ans;while((tmp&1)==0){//終止條件是二進制tmp最低位是1pos++;tmp>>=1;}for(int i=0;i<n;i++){if((a[i]>>pos)&1){//取出第pos位的值x^=a[i];}}y=x^ans;if(x>y) swap(x,y);//從大到小輸出x,yprintf("%d %d\n",x,y); } int main() {int a[8]={1,2,2,3,4,4,5,3};findNum(a,8); }另外一種寫法
#include<stdio.h>void findNum(int *a,int n) {int diff=0;int x=0,y=0;for(int i=0;i<n;i++){diff^=a[i];}diff&=-diff;//取diff的最后一位1的位置for(int i=0;i<n;i++){if((a[i]&diff)==0){x^=a[i];}else y^=a[i];}if(x>y) swap(x,y);printf("%d %d\n",x,y); } int main() {int a[10]={1,2,2,3,4,4,5,3};findNum(a,8); }參考自大神:
微信:ninechapter。
總結
以上是生活随笔為你收集整理的位运算——强大得令人害怕的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 这一晚注定属于C罗!回归首秀梅开二度,现
- 下一篇: 京东api电商接口