题目描述
输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。
思路
首先可以利用原码反码补码的知识直接暴力求解。
源码:首位为符号位,正0负1,后面为真值(绝对值)
反码:正数等于原码,负数除了符号位其余取反
补码:正数等于原码,负数为反码+1
当输入为正数时,直接除2取余法;
当输入为负数时,根据补码和源码的关系来转化成正数。
例如:对于一个8位数,符号位占1位,
补码 | 1000 0001 | -1 | 1000 1001 | -9 |
---|---|---|---|---|
反码 | 1000 0000 | 1000 1000 | ||
原码 | 1111 1111 | -127 | 1111 0111 | -119 |
补码与原码绝对值相加得到128,即2^7。因此输入一个负数,用128相加得到一个正数,例如-119+128=9,1共有2个,再加上符号位一共是3位。
同理,对于一个32位数,除去符号位,还有31个有效位数,即用2^31去与负数相加。
实现
class Solution {
public:
int NumberOf1(int n) {
if (n==0) return 0;
int ret=0;
if (n>0)
{
int m=0;
while (n!=0)
{
m=n%2;
n/=2;
if (m!=0) ret++;
}
}
else
{
ret=NumberOf1(2147483648+n)+1;//2^31相加,最后结果+1(符号位)
}
return ret;
}
};
技巧
对于一个不为0的2进制数,肯定存在若干个1;当对其减1的时候,最右边的1变成0,这个1右边的0全变成1,相当于消掉了原来位置上的1,但是又引入了更多的1,那么如何解决这个引入的问题?只需要跟原来的数做与运算,例如:
1100-1=
1011 再与
1000 这样就消去了最右边的1
如此循环减1下去,直到为0
循环次数即等于1的个数
实现:
class Solution {
public:
int NumberOf1(int n) {
if (n==0) return 0;
int ret=0;
while (n!=0)
{
ret++;
n=n&(n-1);
}
return ret;
}
知识点
原码反码补码