剑指offer-15 二进制中1的个数
微信搜索【程序员画工师】关注更多Java编程技术、数据结构与算法、面试题相关内容。
题目
请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
思路1
转成二进制字符串,遍历获取1的个数
上代码
public class Solution {
public int NumberOf1(int n) {
String num = Integer.toBinaryString(n);
int count = 0;
for(int i = 0;i < num.length();i++){
if(num.charAt(i)=='1'){
count ++;
}
}
return count;
}
}
思路2
先判断整数二进制表示中最右边一位是不是1(把整数和1做位与运算看结果是不是0)。接着把输入的整数右移一位,此时原来处于从右边数起的第二位被移到最右边了,再判断下一位是不是1。这样每次移动一位,直到整个整数变成0为止。
上代码
public class Solution {
public int NumberOf1(int n) {
int count = 0;
while(n != 0){
if((n & 1) == 1){
count ++;
}
n = n >> 1;
}
return count;
}
}
分析
上面的情况是没有考虑到负数的情况的,因为右移运算符m>>n ,右移n位的时候,最右边的n位将被丢弃。如果数字原先是正数,则右移之后在最左边补n个0;如果数字原先是负数,则右移之后在最左边补n个1。例如下面对两个八位二进制数进行右移操作:00001010>>2=00000010 10001010>>3=11110001 上面的方法
如果输入一个负数,比如0x80000000,如果一直做右移运算,最终这个数字就会变成0xFFFFFFFF而陷入死循环。
思路3
既然将目标数右移和1与行不通,那么可以反过来,将1不断左移(1是正数,向左移位不会出现问题,从最低位到最高位每一位依次是1,其他位是0),然后和目标数相与来求1的个数。
上代码
public class Solution {
public int NumberOf1(int n) {
int count = 0;
int flag = 1;
while(flag != 0){
if((n & flag) != 0){
count ++;
}
flag = flag << 1;
}
return count;
}
}
上面思路3的时间复杂度是O(n的位数),n有多少位就要循环多少次。剑指offer中提供了一种非常好的思路,n中有几个1就只需要循环几次。
思路4
分析把一个数减去1的情况,举个例子对11100进行减1操作,那么就变成11011,然后让11011和原数11100做与操作,结果变成了11000,相比之前的11100来说,做这样的一次运算之后,后边少了一个1,继续对11000进行减1操作,那么变成10111,让10111和11000做与操作,变成10000,然后继续对10000进行减1操作,变为01111,让10000和01111做与操作,结果为00000,则结束退出,总共循环3次,1的计数刚好是3,相比方法2的32次,优化不少。
上代码
public class Solution {
public int NumberOf1(int n) {
int count=0;
while (n!=0) {
count++;
n=n&(n-1);
}
return count;
}
}
拓展
把一个整数减去1之后再和原来的整数做位与运算,得到的结果相当于把整数的二进制表示中最右边的1变成0。很多二进制的问题都可以用这种思路解决。
References
[1] 《剑指offer(第二版)》 何海涛著
程序员画工师公众号,获取更多详细Java、Python、C、前端、小程序、产品学习资料,欢迎交流