【剑指Offer学习】【面试题11 :统计一个十进制数中二进制1的个数】

 

分析:

1.很多人看到这个需求的时候,第一反应是先把给定的十进制数转换成二进制数,再把二进制数转换为字符数组,再遍历这个字符数组计算1出现的次数,但是存在一些问题,首先在没有Java相关类库使用的情况下,十进制转换为二进制这个过程应该是很复杂的,并且如果最求算法效率的话,对于数组所需的额外空间以及循环遍历的n次比较的开销是我们所希望避免的。

2.然后我们有相关经验的程序员应该可以想到,既然涉及到了二进制的问题,那么必然少不了Java中的位运算,自然而然地想到了&、|、>>等相关运算符,于是,很多人想到了如下解题方法:

 

public static int getNumber(int n){
        int count = 0;
        while(n!=0){
            if((n&1)==1){
                count++;
            }
            //将n右移一位
            n = n>>1;
        }
        return count;
    }

二进制数与1进行与运算,如果结果得到1说明,该二进制数的最低位为1,则计数加1,然后让n向右移1位,,然后再与1进行与运算。最后美滋滋地得到二进制中1出现的次数。

 

但是:上述的代码忽略了一个致命的问题,就是如果给定的整数位负数,在该数右移操作的时候,高位补的是1,而不是0,逐渐32位的数都会变成1,那么进行与运算就永远得到是1,那么就变成了死循环。

3.在第2步中,总体的思路已经没问题了,那么如何解决上面所出现的问题呢?我们想到位运算中,右移运算与数的正负有关,但是左移运算与数的正负并没有关系,我们可以用到左移运算,这次我们不让给定的整数进行位移运算,我们让1进行左移运算,然后再与给定整数的二进制数进行与运算,这样,就完美解决了整数正负的问题

实现代码如下:

 

public static int getNumber2(int n){
        int count = 0;
        //这是需要进行左移运算的1,其二进制数为
        //0000 0000 0000 0000 0000 0000 0000 0001
        int target = 1;
        //如果1左移到最左端,所得得为 0000 0000 0000 0000 0000 0000 0000 0000 为0,则跳出循环
        while(target !=0){
            if((n&target) !=0){
                count++;
            }
            //左移运算
            target = target<<1;
        }
        return count;
    }

 

 

4.除了3中的解题方法外,再介绍一种更为高效的方法

如果将一个二进制数-1,那么该二进制数最右侧的1将会变成0,1后面的0均变成1,1前面的数保持不变

也就是说,如果把一个二进制数-1,那么概述最右侧的1及1右侧的所有书将改变(0-->1或者1--->0)

如果把这个数和原数进行与运算,那么最右侧的那个1前面的1将不变,1以及1右侧的所有书将变成0,也就是说进行一次上述运算后,原数最右侧的那个1会变成0,那么只要重复上述操作,当原数变成0时,循环就是1的个数

 

实现代码

 

public static int getNumber3(int n){
        int count =0;
        while(n!=0){
            n=n&(n-1);
            count++;
        }
        return count;
    }

 

 

 

 

 



 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值