1、判斷奇偶數
如果把一個數n以二進制數的形式表示的話,我們只需要判斷最后一個二進制位是1還是0即可。如果是1,則代表奇數,否則為偶數。代碼如下:
if(n & 1 == 1){
// n是奇數
}
2、交換兩個數
x = x ^ y; // (1)
y = x ^ y; // (2)
x = x ^ y; // (3)
我們都知道兩個相同的數異或之后的結果為0,即 n ^ n = 0,並且任何數與0異或之后等於它本身,即 n ^ 0 = n。
於是我們把(1)中的x代入(2)中的x,有:y = x ^ y = (x ^ y) ^ y = x ^ ( y ^ y) = x ^ 0 = x,這樣x的值就賦給了y。
對於(3),推導如下:x = x ^ y = (x ^ y) ^ x = (x ^ x) ^ y = 0 ^ y = y,這樣y的值就賦給了x。
異或運算支持運算的交換律和結合律。
3、找出沒有重復的數
給你一組整型數據,這些數據中,其中有一個數只出現了一次,其他的數都出現了兩次,讓你來找出一個數
這道題可能很多人會用一個哈希表來存儲,每次存儲的時候,記錄下某個數出現的次數,最后遍歷哈希表找出只出現了一次的次數。這種方式的時間復雜度是O(n),空間復雜度也為O(n)了。
其實這道題也可以進行位運算,,我們可以把這一組整型數全都異或,由於兩個相同的數異或的結果為0,一個數與0異或的結果為其自身,所以異或的所得到的結果就為只出現了一次的數。例如這組數據是:1, 2, 3, 4, 5, 1, 2, 3, 4。其中 5 只出現了一次,其他都出現了兩次,把他們全部異或一下,結果如下:
1 ^ 2 ^ 3 ^ 4 ^ 5 ^ 1 ^ 2 ^ 3 ^ 4 = (1 ^ 1) ^ (2 ^ 2) ^ (3 ^ 3) ^ (4 ^ 4) ^ 5 = 0 ^ 0 ^ 0 ^ 0 ^ 5 = 5
代碼如下:
int find(int[] nums){
int tmp = nums[0];
for(int i = 1;i < nums.length; i++)
tmp ^= arr[i];
return tmp;
}
4、2的n次方
求解 2 的 n 次方,並且不能使用系統自帶的 pow 函數
很多人看到這個題可能覺得讓n個2相乘就行了,如果這么做的話,時間復雜度為O(n)了。那么如何用位運算來做呢?
比如n = 13,n的二進制數表示為1101,那么2的13次方可以拆解為:2 ^ 1101 = 2 ^ 0001 * 2 ^ 0100 * 2 ^ 1000。我們可以通過 & 1和 >>1 來逐位讀取 1101,為1時將該位代表的乘數累乘到最終結果。最終代碼如下:
int pow(int n) {
int sum = 1;
int tmp = 2;
while(n != 0) {
if(n & 1 == 1)
sum *= tmp;
temp *= temp;
n >>= 1;
}
return sum;
}
5、找出不大於N的最大的2的冪指數
例如 N = 19,那么轉換成二進制就是 00010011(這里為了方便,我采用8位的二進制來表示)。那么我們要找的數就是,把二進制中最左邊的 1 保留,后面的 1 全部變為 0。即我們的目標數是 00010000。那么如何獲得這個數呢?相應解法如下:
1、找到最左邊的 1,然后把它右邊的所有 0 變成 1
2、把得到的數值加 1,可以得到 00100000即 00011111 + 1 = 00100000。
3、把 得到的 00100000 向右移動一位,即可得到 00010000,即 00100000 >> 1 = 00010000。
那么問題來了,第一步中把最左邊 1 中后面的 0 轉化為 1 該怎么才能獲得呢?
代碼如下:
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
就是通過把 n 右移並且做或運算即可得到。我解釋下吧,我們假設最左邊的 1 處於二進制位中的第 k 位(從左往右數),那么把 n 右移一位之后,那么得到的結果中第 k+1 位也必定為 1,然后把 n 與右移后的結果做或運算,那么得到的結果中第 k 和 第 k + 1 位必定是 1;同樣的道理,再次把 n 右移兩位,那么得到的結果中第 k+2和第 k+3 位必定是 1,然后再次做或運算,那么就能得到第 k, k+1, k+2, k+3 都是 1,如此往復下去….
最終的代碼如下:
int findN(int n){
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8 // 整型一般是 32 位,上面我是假設 8 位。
return (n + 1) >> 1;
}
這種做法的時間復雜度近似為 O(1)。