若是把一個數n以二進制數的形式表示的話,咱們只須要判斷最後一個二進制位是1仍是0便可。若是是1,則表明奇數,不然爲偶數。代碼以下:java
if(n & 1 == 1){ // n是奇數 }
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。code
對於(3),推導以下:x = x ^ y = (x ^ y) ^ x = (x ^ x) ^ y = 0 ^ y = y,這樣y的值就賦給了x。class
異或運算支持運算的交換律和結合律。遍歷
給你一組整型數據,這些數據中,其中有一個數只出現了一次,其餘的數都出現了兩次,讓你來找出一個數二進制
這道題可能不少人會用一個哈希表來存儲,每次存儲的時候,記錄下某個數出現的次數,最後遍歷哈希表找出只出現了一次的次數。這種方式的時間複雜度是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時間
代碼以下:while
int find(int[] nums){ int tmp = nums[0]; for(int i = 1;i < nums.length; i++) tmp ^= arr[i]; return tmp; }
求解 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; }
例如 N = 19,那麼轉換成二進制就是 00010011(這裏爲了方便,我採用8位的二進制來表示)。那麼咱們要找的數就是,把二進制中最左邊的 1 保留,後面的 1 所有變爲 0。即咱們的目標數是 00010000。那麼如何得到這個數呢?相應解法以下:
一、找到最左邊的 1,而後把它右邊的全部 0 變成 1
二、把獲得的數值加 1,能夠獲得 00100000即 00011111 + 1 = 00100000。
三、把 獲得的 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)。