Given a positive integer, output its complement number. The complement strategy is to flip the bits of its binary representation.
Note:
- The given integer is guaranteed to fit within the range of a 32-bit signed integer.
- You could assume no leading zero bit in the integer’s binary representation.
Example 1:
Input: 5 Output: 2 Explanation: The binary representation of 5 is 101 (no leading zero bits), and its complement is 010. So you need to output 2.
Example 2:
Input: 1 Output: 0 Explanation: The binary representation of 1 is 1 (no leading zero bits), and its complement is 0. So you need to output 0.
Subscribe to see which companies asked this question
【題目分析】
給定一個正整數,對該數的二進制表示形式,從最高位的1開始向后按位取反。
【思路】
如果我們能知道該數最高位的1所在的位置,就可以構造一個長度和該數據所占位置一樣長的一個掩碼mask,然后概述和mask進行異或即可。
例如:5的二進制是101,我們的構造的掩碼為mask=111,兩者異或則為010,即是所要的結果。
【java代碼1】
1 public class Solution { 2 public int findComplement(int num) { 3 int mask = 1, temp = num; 4 while(temp > 0) { 5 mask = mask << 1; 6 temp = temp >> 1; 7 } 8 return num^(mask-1); 9 } 10 }
【java代碼2】
1 public class Solution { 2 public int findComplement(int num) { 3 int mask = (Integer.highestOneBit(num) << 1) - 1; 4 return num^mask; 5 } 6 }
Integer.highestOneBit(i)是一個什么樣的函數呢?
首先來補充一點背景知識。
1、在計算機系統中,數值一律使用補碼來表示和存儲。主要原因是使用補碼可以將符號位和其它位統一處理;同時,減法也可按照加法來處理。另 外, 兩個用補碼表示的數相加時,如果最高位(符號位)有進位,則進位被舍棄。
- 補碼與原碼的轉換過程幾乎相同。
- 數值的補碼表示(分兩種)
- 正數的補碼:與原碼相同
- 負數的補碼:符號位位1,其余位位該數絕對值的原碼按位取反;然后整個數加1
- 已知一個數的補碼,求原碼的操作分為兩種情況
- 如果補碼的符號位“0”,表示是一個正數,所以補碼就是該數的原碼
- 如果補碼的符號位為“1”,表示是一個負數,求原碼的操作可以是:符號位位1,其余各位取反,然后整個數加1。
2、移位運算符就是在二進制的基礎上對數字進行平移。Java按照平移的方向和填充數字的規則分為三種:<<左移,>>帶符號右移 和>>>無符號右移。
3、 在Java的移位運算中,byte、short和char類型移位后的結果會變成int類型,對於byte、short、char和int進行移位時,對於char、short、char和int進行移位操作時,規定實際移動的次數是移動次數和32的余數,也就是移位33次和移位1次得到的結果相同。移動long型的數值時,規定實際移動的次數是移動次數和64的余數,也就是移動65次移位1次得到相同的結果。
(1) << 運算規則:按二進制形式吧所有的數字向左移動對應的位數,高位移出(舍棄),低位的空位補零。
語法格式:
需要移位的數字<<移位的次數
例如:4<<2 ,則是將數字4左移2位
計算過程
4<<2
Java中一個int數占四個字節,那么4的二進制數字為00000000 00000000 00000000 00000100,然后把該數字左移兩位。其它的數字都朝右平移兩位,最后在低位(右側)的兩個空位補零。則得到的最終結果是00000000 00000000 00000000 00010000,即轉換為十進制數16。
在數字沒有溢出的前提下,對於正數和負數,左移一位都相當於乘以2的1次方,左移n位就相當於乘以2的n次方。
在溢出的前提前,則不符合這個規律。讀者可以嘗試輸出(long)1610612736*4和1610612736<<2這兩個結果進行比對。
(2)>>運算規則:按二進制形式吧所有的數字都向右移動對應的位置,低位移出(舍棄),高位的空位補符號位,即正數補零,負數補1。
語法格式:
需要移位的數字>>移位的次數
例如:-4>>2和4>>2,則是將數字 -4和4右移2位
計算過程
4>>2
Java中一個int數占四個字節,同樣4的二進制為00000000 00000000 00000000 00000100,然后把該數字右移兩位。其它的數字都朝左平移兩位,最后在高位補符號位(該數是正數,全補零),得到的結果是00000000 00000000 00000000 00000001,即使十進制的1。數學意義就是右移移位相當於除2,右移n位相當於除以2的n次方。
4>>2
由於負數在計算機中是以補碼的形式存儲的,那么-4的二進制為11111111 11111111 11111111 11111100,然后把該數字右移兩位,其它數字都朝左平移兩位,最后在高位補符號位(該數是負數,全補一),得到的結果是11111111 11111111 11111111 11111111(補碼格式),即是十進制的-1。
(3)>>>運算規則:按二進制形式吧所有的數字向右移動對應的位數,低位移出(舍棄),高位的空位補零。正數運算結果與帶符號右移相同,對於負數來說則不同。
對於4>>>2和-4>>>2運算,可以通過上述例子進行類推。
了解了Java的位運算之后,來看下 Integer.highestOneBit (i) 這個函數的實現代碼:
1 publicstaticinthighestOneBit(int i) { 2 // HD, Figure 3-1 3 i |= (i >> 1); 4 i |= (i >> 2); 5 i |= (i >> 4); 6 i |= (i >> 8); 7 i |= (i >> 16); 8 return i - (i >>> 1); 9 }
1、第一步的作用是把最高位1右移移位,並與原數據按位取或。那么這就使得最高位和它的下一位是連續兩個1。
2、第二步的作用是把剛剛移位得到連續兩個1繼續右移兩位並與原數據按位取或。那么這就使得最高兩位和它的下兩個連續位組成四個連續的1。
3、 以此類推,最終得到的i是從開始的最高位到結束全是1。並減去i不帶符號的右移一位,即可得到一個int數據的最高位的值。
4、上述情況是針對於i不為零和負數的情況,如果i為零,那么得到的結果始終為零。如果i位負數,那么得到的結果始終是-2147483648。即等於Integer.MIN_VALUE。(原因在於負數的最高位始終為1,即是負數的符號位)
此函數的最重要理解點在與要始終把握二進制的最高位進行運算處理,那么對於函數中的右移一位、兩位、四位、八和十六位就好理解了。同理,對於long類型的取最高位運算應該需要加一條語句
i|=(i>>32); 原因在於long類型在Java中是64位的。
Long類型的hightestOneBit(i)代碼如下:
Long類型的hightestOneBit(i)代碼如下:
1 public static long highestOneBit(long i) { 2 // HD, Figure 3-1 3 i |= (i >> 1); 4 i |= (i >> 2); 5 i |= (i >> 4); 6 i |= (i >> 8); 7 i |= (i >> 16); 8 i |= (i >> 32); 9 return i - (i >>> 1); 10 }