基礎知識
位運算是把數字用二進制表示之後,對每一位上0或1的運算。位運算總共只有5種運算:與、或、異或、左移和右移。對於異或操作,需要注意一下0^0=0; 1^1=0; 1^0=1
。
應用:二進制中1的個數
題目:請實現一個函數,輸入一個整數,輸出該數二進制表示中1的個數。例如把9表示成二進制是1001,有2位是1.因此如果輸入9,該函數輸出2。
分析對於負數1000 0000
右移一位的時候,得到1100 0000
而非0100 0000
。因此如下方法對於負數會進入死循環:
int NumberOf1(int n) {
int count=0;
while(n){
if(n&1) count++;
n=n>>1;
}
return count;
}
代碼實現
//解法一,不右移n,左移1
int NumberOf1(int n) {
int count=0;
unsigned int flag=1;
while(n){
if(n&flag) count++;
flag=flag<<1;
}
return count;
}
//解法二,基於這樣一個規律:我們把一個整數減去1,都是把最右邊的1變成0;並且如果它右邊還有0的話,所有的0都變成1,而它左邊所有位都保持不變。
int NumberOf1(int n) {
int count=0;
while(n){
++count;
n=(n-1)&n;
}
return count;
}
應用:不用加減乘除做加法
題目:寫一個函數,求兩個整數之和,要求在函數體內不得使用+、-、*、÷四則運算符號。
分析:分三步:第一步只做各位相加不進位,第二步做進位,第三步將前面兩個結果加起來,即重複前兩位,直到不產生進位爲止。
代碼實現
int Add(int num1, int num2) {
int sum, carry;
while (1) {
sum = num1^num2;
carry = (num1&num2) << 1;
num1 = sum;
num2 = carry;
if (num2 == 0) return num1;
}
}
測試用例
Add(5, 17);
Add(0, 17);
Add(5, 0);
應用:數組中只出現一次的數字
題目:一個整型數組裏除了兩個數字之外,其他的數字都出現了兩次。請寫程序找出這兩個只出現一次的數字。要求時間複雜度是O(n),空間複雜度是O(1)。
分析:首先,大家都對“數組中只有一個只出現一次的數字”這個題目比較熟悉,直接遍歷一遍異或即可得解。出現兩個數字怎麼辦呢?至少有一種思路是將這個數組分爲兩個數組,每個數組只有一個只出現一次的數字。問題又來了,怎麼得到這兩個數組呢?答:異或遍歷一遍,得到的即爲兩個只出現一次數字的異或值;這個異或值若某一位上爲1,那需要找的兩個數在這一位上必定不相同;因此可以根據這一位是否爲1將原數組分爲兩個數組。
代碼實現
int findFirstBitOfOne(int num){
int count=0;
int flag=1;
while(num & flag==0){
count++:
flag =flag<< 1;
}
return count;
}
void findNumsOnlyOnce(vector<int> array, int& num1, int& num2){
int len=array.size(); //假設輸入數據符合規則
if(len<2){
num1=array.at(0);
num2=array.at(1);
return;
}
int nor=0;
for(int i=0; i<len; i++){
nor ^= array.at(i);
}
int flag= 1 << findFirstBitOfOne(nor);
num1=0;num2=0;
for(int i=0; i<len; i++){
if(array.at(i)&flag == 0){
num1 ^= array.at(i);
}
else{
num2 ^= array.at(i);
}
}
return;
}