题目10:二进制中1的个数(leetcode链接:https://leetcode-cn.com/problems/er-jin-zhi-zhong-1de-ge-shu-lcof/)
一、问题描述
题目:统计二进制中1的个数。
写一个函数返回参数二进制中 1 的个数。
示例: 15 0000 1111 4 个 1
-1 ................ 32个1
示例分析:
二进制负数,在内存中以其补码的方式进行存储。
-1的原码:10000000 00000000 00000000 00000001(第一位为符号位)
-1的反码:11111111 11111111 11111111 11111110
-1的补码:11111111 11111111 11111111 11111111
所以,-1的二进制中有32个1
原码、补码、反码
原码:数的二进制表示
反码:原码除符号位外的位数按位取反
补码:反码 + 1
二、问题分析及C语言程序描述
方法1:可能会引起死循环的解法
判断所给数的最后一位是不是1,如果是count++,而判断该数的最后一位是否为1只需要将该数与1相与如果结果是1表明最后一位是1,然后将该数右移一位继续判断最后一位。
缺陷分析:右移运算符,如果目标数是正数则右移n位为去掉该数的最右边的n位左边补n个0;如果目标数是负数,则右移n位为去掉最右边的n位左边补n个1,因此对于一个负数如果采取上述方法,每次给左边补1,就会陷入死循环。而如果把右移改成/2运算,在计算机中除法的运算效率比右移的运算效率低的多。
方法2
每次右移时,如果是负数会陷入死循环。如果最右边1位是1该数与1相与结果是1,如果最右边的第二位是1该数与2相与结果为2......如果最左边一位是1,该数与2^31相与结果为2^31.因此可以使用for循环,进行32 次相与判断每位是否为1.
int hammingWeight(uint32_t n) {
int count = 0;
unsigned int flag = 1;
while(flag){
if(n&flag){
count++;
}
flag = flag << 1;
}
return count;
}
方法3
方法2中,无论所给数中有几个1,都需要进行32次判断。对于一个整数,减去1后,我们发现二进制中最右边的一个1变成了0,如果这个1的右边还有0则会变成1,例1100-1 = 1011,而一个整数减去1在与原整数相与,会将整数的最右边一位1变成0,例1100 & 1011 = 1000,而1100最右边一位1去掉结果为1000.我们重复这样的操作,直到该整数变成0,那么整数中有几个1就需要进行几次这样的操作。因此,这种方法中整数中有几个1就需要判断几次。
int hammingWeight(uint32_t n) {
int count = 0;
while(n){
++count;
n = n&(n-1);
}
return count;
}