剑指 Offer 15. 二进制中1的个数
请实现一个函数,输入一个整数(以二进制串形式),输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。
示例 1:
输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 ‘1’。
示例 2:
输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 ‘1’。
示例 3:
输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 ‘1’。
提示:
输入必须是长度为 32 的 二进制串 。
思路
该题是考察二进制运算相关知识点
俗话说会者不难,难者不会
带大家复习一下有关二进制的操作
二进制的操作有且仅有5种操作:
位与 &
位或 |
位异 ^
左移 <<
右移 >>
位运算符:& | ~ ^ << >>
按位与 & 1假即假
按位或 | 1真即真
按位非 ~ 真变假,假变真
左移<< 原数乘以进制^ 移动位数。举例:十进制239,左移2位,23900,即239 *10^2
右移>>原数除以进制^ 移动位数。举例:十进制138,右移3位,0.138,即138 *10^-3
需要注意的是:
左移运算符 m << n表示:把m左移n位,在左移n位的时候,最左边的n位将被丢弃,同时在最右边补上n个0
右移运算符 m >> n表示:把m右移n位,在右移n位的时候,最右边的n位将被丢弃。但是,右移时处理最左边的情形要稍微复杂一点。
如果数字是一个无符号数值,则用0填补最左边的n位;
如果数字是一个有符号数值,则用数字的符号位填补最左边的n位。
也就是说:
如果数字原来是一个正数,则右移之后在最左边补n个0;
如果数字原来是一个负数,则右移之后在最左边补n个1;
知道了二进制的相关操作,那么,如何判断某一位是否为1呢?
这就要使用位运算符中的:位与&了
如果某一位的值为0,则&1,该位为0;
如果某一位的值为1,则&1,该位为1。
从而可以判断出该位是0或者1
在此基础上,我们可以考虑让n每次左移一位,然后与1做比较
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int count = 0;
while(n != 0){
if((n & 1) != 0){
count++;
}
n = n >>> 1;
}
return count;
}
}
java中有三种移位运算符
<< : 左移运算符,num << 1,相当于num乘以2
>> : 右移运算符,num >> 1,相当于num除以2
>>> : 无符号右移,忽略符号位,空位都以0补齐
以上解答,n = n >>> 1;可以
但是,修改为n = n >> 1;就不可以了
原因是,如果 n = n >> 1,那么如果n是负数,那么while(n != 0)一直成立,然后造成死循环
如果是C语言下,并没有>>>无符号右移操作,那么,在C语言下,又如何解决呢?
我们观察到,上面的写法是在移动n,每次将n右移一位
我们可以设置一个值flag,令flag = 1,每次将flag左移一位,做比较,效果与上面是一样的,而且,可以计算出负数情况下1的个数。
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int flag = 1;
int count = 0;
while(flag != 0){
if((n & flag) != 0){
count++;
}
flag = flag << 1;
}
return count;
}
}
注意:
一开始写成了while(flag > 0)或者while(flag >= 0)都是不对的
如果n是负数,那么,flag最后也是负数
此时,如果使用>=0,则flag为负数,不会执行while循环,因此,n的最高位1没有被计算进去