剑指offer(15 二进制中1的个数) 题解

剑指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、前端、小程序、产品学习资料,欢迎交流

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值