题目描述
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
思路分享
此题在熟练掌握计组课上的编码基础后很好解决,难度不大。
设输入的十进制数为n,二进制形式的1个数为cnt。
-
正数实现
正数按照常见的10转2进制方法实现即可:求n%2 = 1 则cnt加1;再另n = n / 2(整除);循环直至n == 0.
-
负数实现
负数稍微麻烦一些,按照定义,负数的补码为2^32 - |负数|的差的原码。
题设的int变量大小为32位,以-1的补码为例:
2^32原码为1 0000 0000 0000 0000 0000 0000 0000 0000;
|-1| = 1,原码为0000 0000 0000 0000 0000 0000 0000 0001;
则2^32 -1= 1111 1111 1111 1111 1111 1111 1111 1111;
由于int变量32位的限制,int无法表达2^32 这一数值。
因此只计算至与|n|最接近且比|n|大的2次幂整数,设它为full, 如:
十进制8(1000)的full为16=2^4(10000);
十进制17(10001)的full为32=2^5(100000);
full-|n|的差得到的正数的原码即为n的补码(低位部分)。
如:2^5 - 17 = 100000 - 10001 = 01111
按照补码的规定,32位表达时,负数原码为0的高位全部转为1,因此-17的补码表示为:
1111 1111 1111 1111 1111 1111 1110 1111
即-17的补码高32-5=27位全为1.
特别的,有-2^32(即-2147483648)补码为:
1000 0000 0000 0000 0000 0000 0000 0000
代码实现
class Solution {
public:
int NumberOf1(int n) {
if(n==0) return 0;
if(n==-2147483648) return 1;
int cnt;
if(n > 0){
while(n >= 1){
if(n%2 == 1) cnt++;
n /= 2;
}
}
if(n < 0){
int temp = 0-n, full=1, time=0;
//full是最近的2次幂数,time是log2(full)
while(temp >= 1) {
temp/=2;
full*=2;
time++;
}
//full减去这个负数得到的就是补码
n = full + n;
while(n >= 1){
if(n%2 == 1) cnt++;
n /= 2;
}
//由于int为32位,所以比full更高的位都要变成1
cnt = cnt + (32 - time);
}
return cnt;
}
};