題目:輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼錶示。
本題考查的是位運算,先大概回憶下位運算的基本概念。
1.算術左移和邏輯左移
算術左移和邏輯左移一樣都是右邊補0:
比如 00101011
算術左移一位:01010110
邏輯左移一位:01010110
對於二進制的數值來說左移n位等於原來的數值乘以2的n次方
比如00011010十進制是26,左移兩位後是01101000轉成十進制是104恰好是26的4倍。
ps:這種倍數關係只適用於左移後被捨棄的高位不含1的情況,否則會溢出。
2.算術右移和邏輯右移
邏輯右移很簡單,只要將二進制數整體右移,左邊補0即可:
如10101101邏輯右移一位爲01010110
算術右移符號位要一起移動,並且在左邊補上符號位,也就是如果符號位是1就補1符號位是0就補0
比如:11100算術右移一位爲11110(符號位1跟着一起移動並且左邊補了1)
對於二進制的數值來說右移n位等於原來的數值除以2的n次方
比如10110100十進制是76(需要先將這個補碼轉換成原碼之後再轉換成十進制),右移兩位後是11101101轉成十進制是19恰好是76的4倍。
ps:這種倍數關係只適用於右移後被捨棄的低位不含1的情況,否則每舍一次1則代表餘數被捨去,保留整數部分。
3.C++中的位運算
左移都是右邊補0,所以主要說下右移中算數右移和邏輯右移的區別。
C/C++語言中邏輯右移和算數右移共享同一個運算符>>。編譯器決定使用邏輯右移還是算數右移,根據的是運算數的類型。如果運算數類型是unsigned則採用邏輯右移,而signed則採用算數右移。
在本題中,需要讓負數使用邏輯右移操作,由於C/C++ 語言中邏輯右移和算數右移共享同一個運算符>>,因此此處使用運算符>>對負數進行移位得到的是算術右移,最高位一直補1,會進入死循環。因此使用C++ 進行本題的解答需要一點小小的處理,如果是Java等可以直接使用>>>進行邏輯右移,無C++語言特性的影響。
思路:首先判斷n是不是負數,當n爲負數的時候,直接用後面的while循環會導致死循環,因爲負數向右移位的話最高位補1 。因此需要一點點特殊操作,可以將最高位的符號位1變成0,也就是n & 0x7FFFFFFF,這樣就把負數轉化成正數了,唯一差別就是最高位由1變成0,因爲少了一個1,所以count加1,之後再按照while循環裏處理正數的方法來操作就可以了。
C++代碼如下:
class Solution{
public:
int NumberOf1(int n){
int num = 0;
if (n < 0){
n = n & 0x7FFFFFFF;
++num;
}
while (n != 0){
if (n & 1 == 1){
num++;
}
n = n >> 1;
}
return num;
}
};
位運算概念部分參考博客:https://blog.csdn.net/yddj5/article/details/52822366