4.繼續刷題:不用額外的變數交換兩個整數的值(異或應用)
5. 不用任何比較判斷找出兩個數中較大的數
6. 只用位運算不用算術運算實現整數的加減乘除運算
請實現乙個函式,輸入乙個整數,輸出該數二進位制表示中 1 的個數。例如,把 9 表示成二進位制是 1001,有 2 位是 1。因此,如果輸入 9,則該函式輸出 2。
輸入:00000000000000000000000000001011
輸出:3
解釋:輸入的二進位制串 00000000000000000000000000001011 中,共有三位為 『1』。
演算法:在分析這種演算法之前,我們先來分析把-乙個數減去1的情況。如果乙個整數不等於0,那麼該整數的二進位制表示中至少有一位是1。先假設這個數的最右邊一位是1,那麼減去1時,最後一位變成0而其他所有位都保持不變。也就是最後一位相當於做了取反操作,由1變成了0。
接下來假設最後一位不是1而是0的情況。如果該整數的二進位制表示中最右邊的1位於第m位,那麼減去1時,第m位由1變成0,而第m位之後的所有0都變成1,整數中第m位之前的所有位都保持不變。舉個例子:乙個二進位制數1100, 它的第二位是從最右邊數起的-乙個1。減去1後,第二位變成0,它後面的兩位0變成1,而前面的1保持不變,因此得到的結果是1011。
在前面兩種情況中,我們發現把乙個整數減去1,都是把最右邊的1變成0。如果它的右邊還有0,則所有的0都變成1,而它左邊的所有位都保持不變。接下來我們把-乙個整數和它減去1的結果做位與運算,相當於把它最右邊的1變成0。還是以前面的1100為例,它減去1的結果是1011。我們再把1100和1011做位與運算,得到的結果是1000。 我們把1100最右邊的1變成了0,結果剛好就是1000。
我們把上面的分析總結起來就是:把乙個整數減去1,再和原整數做與運算,會把該整數最右邊的1變成0。那麼乙個整數的二二進位制表示中有多少個1,就可以進行多少次這樣的操作。基於這種思路,我們可以寫出新的**:(參考劍指offer)
/**
* 方法2:由於n和n-1作與&比較的時候,最右邊的第乙個1會變成0,而這個1後邊的0會變成1,1會變成0
* 把乙個整數減去1,再和原整數做與運算,會把該整數最右邊的1變成0。那麼乙個整數的二二進位制表示中有多少個1,
* 就可以進行多少次這樣的操作。基於這種思路,我們可以寫出新的**:
*/public
inthammingweight2
(int n)
return count;
}
int count =0;
while
(n !=0)
n = n >>1;
}return count;
/**
* 給定乙個整數,編寫乙個函式來判斷它是否是 2 的冪次方。
* 輸入: 16
* 輸出: true
* 解釋: 2^4 = 16
* 演算法:乙個整數只要是2的冪次方,那麼他的二進位制表示中肯定就只有1個1,
* 因此,對這個數的二進位制表示減1的時候,原來1的位置會變成0,原來是0的地方會變成1
* 將減1的數字與源數字進行與運算,如果源數字只有1個1的時候,那麼與運算後,整個數會變成0
*/public
boolean
ispoweroftwo
(int n)
long x =
(long
)n;if
((x &
(x -1)
)==0)
return
false
;}
把乙個整數減去1之後再和原來的整數做位與運算,得到的結果相當於把整數的二進位制表示中最右邊的1變成0。很多二進位制的問題都可以用這種思路解決。
按位取反:~ :二進位制位中的0變1,1變0
左移運算子m<00001010<< 2 = 00101000
10001010<< 3 = 01010000
右移運算子m>>n表示把m右移n位。在右移n位的時候,最右邊的n位將被丟棄。但右移時處理最左邊位的情形要稍微複雜-一點。如果數字是乙個無符號數值, 則用0填補最左邊的n位:如果數字是乙個有符號數值,則用數字的符號位填補最左邊的n位。也就是說,如果數字原先是乙個正數,則右移之後在最左邊補n個0;如果數字原先是負數,則右移之後在最左邊補n個1。下面是對兩個8位有符號數進行右移的例子:
00001010>> 2 = 00000010如何不用任何額外變數交換兩個整數的值?10001010>> 3 = 11110001
說明:異或^,其實表示的是儲存兩個元素的不同屬性,0 ^ 0 = 0;1 ^ 0 = 1; 0 ^ 1 = 1;1 ^ 1 = 0
明顯地,可以看出,0表示的是相同的屬性,1表示的是不同的屬性
使用異或運算交換兩個數字,第一次使用a記錄兩者的不同資訊,第二次使用b記錄與b相反的資訊,第三次使用a記錄與新b相反的資訊,**:
/**
* 編寫乙個函式,不用臨時變數,直接交換numbers = [a, b]中a與b的值。
* 演算法:
* 如果給定整數a和b,用以下三行**即可交換a和b的值。
* a=a^b;
* b=a^ b;
* a=a^ b;
* 如何理解這三行**的具體功能呢?首先要理解關於異或運算的特點:
* 假設a異或 b的結果記為c,c就是a整數字資訊和b整數字資訊的所有不同資訊。
* 比如,a=4=100, b=3=011,a^b=c=111。
* a異或c的結果就是b。比如a=4=100, c= =111, a^c=011=3=b。
* b異或c的結果就是a。比如b=3=011, c= =111, b^c=100=4=a。
*/public
int[
]swapnumbers
(int
numbers)
public
intflip
(int n)
public
intsign
(int n)
public
intgetmax1
(int a,
int b)
sign函式的功能是返回整數n的符號,正數和0返回1,負數則返回0。flip 函式的功能是如果n為1,返回0,如果n為0,返回1。所以,如果a-b的結果為0或正數,那麼sca為1,scb為0;如果a-b的值為負數,那麼sca為0,scb為1。sca和scb必有乙個
為1,另乙個必為0。所以returna* sca+b* scb;,就是根據a-b的值的狀況,選擇要麼返回a,,要麼返回b。
但方法一是有侷限性的,那就是如果a-b的值出現溢位,返回結果就不正確。
第二種方法可以徹底解決溢位的問題,也就是如下**中的getmax2方法。
public
intflip
(int n)
public
intsign
(int n)
public
intgetmax2
(int a,
int b)
借鑑左神的程式設計師面試指南:
}解釋(劍指offer):
位運算 刷題總結
位運算 從現代計算機中所有的資料二進位制的形式儲存在裝置中。即 0 1 兩種狀態,計算機對二進位制資料進行的運算 都是叫位運算。判斷兩個數是否異號 int x 1 y 2 bool f x y 0 true int x 3,y 2 bool f x y 0 false 這個技巧還是很實用的,利用的是...
位運算題 刷題總結
給定乙個整數陣列 nums,其中恰好有兩個元素只出現一次,其餘所有元素均出現兩次。找出只出現一次的那兩個元素。示例 輸入 1,2,1,3,2,5 輸出 3,5 方法 異或 我知道異或可以使所有一對一對的值都消失,得到只出現過一次的數的異或值,但是這題的重點是,你得到兩個只出現一次的數之後,如何區分?...
刷題 位運算
includeusing namespace std 不使用中間變數交換兩個整數的值 void swap int a,int b 整數的二進位制表達中有多少個1 int number of one int n return result 在乙個整數陣列中,只有1個數出現了奇數次,其餘數都出現了偶數次...