剑指Offer(Java版):二进制中的1的个数

题目:请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。例如把9表示成二进制是1001,有2位是1,所以若是输入9,该函数输出2.面试

一、可能引发死循环的解法算法

这是一道很基本的考察二进制和位运算的面试题。题目不是很难,面试官 提出问题以后,咱们很快造成一个基本的思路:先判断证书二进制表示中最右边一位是否是1.接着把输入的证书右移一位,此时原来处于从右边树起的第二位被移 到最后一位,再判断是否是1.这样没移动一位,知道整个整数变成0为止。如今的问题变成怎么判断一个整数的最右边是否是1了。这很简单,只要把整数和1作 位与运算看结果是否是0就知道了。1除了最右边的一位以外全部的位都是0.基于这个思路,咱们很快写出这样的代码:编程

package cglib;函数

public class List1
{  
    public static int numberOf1(int num) {  
        int count = 0;spa

        while(num!=0){code

        if((num&1)!=0)数学

        count++;class

        num = num>>1;效率

        }扩展

        return count;
    }  
 
    public static void main(String[] args) {  
        
        System.out.println(numberOf1(9));  
        System.out.println(numberOf1(-9));
}
}

只输出了2

负数死循环

面试官看了 代码后可能会问:把证书右移一位和把整数除以2在数学上是等价的,那上面的代码中能够把右移换成除以2吗?答案是否认的。由于除法的效率比移位运算要低不少,在实际编程中应尽量地用移位运算代替乘除法。

面试官会问第二个问题就是:上面的函数若是输入一个负数,好比 0x80000000,运行的时候会发生什么状况呢?把负数0x80000000右移一位的时候,并非简单地把最高位的1移到第二位变成 0x40000000,而是0xC0000000.这是由于移位前是个负数,仍然保证移位是个负数,所以移位后的最高位会设为1.若是一直作右移位运算, 最终这个数字会编程0xFFFFFFFF而陷入死循环。

二、常规解法:

为了不死循环,咱们能够不右移输入的数字n.首先把n和1作与运算,判断n的最低位是否是1.接着把1左移一位获得2,再和n作与运算,就能判断n的次低位是否是1。。。。这样反复左移,每次都能判断n的其中一位是否是1.基于这种思路,咱们能够写出这样的代码:

package cglib;

public class List1
{  
    public static int numberOf1(int num) {  
        int count = 0;

        int flag= 1;

        while(flag!=0){

        if((num&flag)!=0)

        count++;

        flag =flag <<1;

        }

        return count;
    }  
 
    public static void main(String[] args) {  
        
        System.out.println(numberOf1(9));  
        System.out.println(numberOf1(-9));
}
}

输出:

2
31

 

这个解法中循环的次数等于二进制中的位数,32位的整数须要循环32次,下面咱们再介绍一个算法,整数中有几个1就只循环几回。

 

三、能给面试官带来惊喜的算法。

咱们的分析就是:把一个整数减去1,再和原整数作与运算,会把该整数最右边的一个1变成0.那么一个整数的二进制表示中有多少个1,就能够进行多少次运算。基于这种思路,咱们能够写出这样的代码:

package cglib;

public class List1
{  
    public static int numberOf1(int num) {  
        int count = 0;  
        while (num != 0) {  
            count++;
            System.out.println("与前:num="+num);//1001
            num = num & (num - 1);//00001001&00001000=00001000,00001000&0000111=00000000
            System.out.println("与后num="+num);
            System.out.println("count="+count);
 
        }  
        return count;  
    }  
 
    public static void main(String[] args) {  
        
        System.out.println(numberOf1(9));  
        System.out.println(numberOf1(-9));
}
}

输出:

与前:num=9
与后num=8
count=1
与前:num=8
与后num=0
count=2
2
与前:num=-9
与后num=-10
count=1
与前:num=-10
与后num=-12
count=2
与前:num=-12
与后num=-16
count=3
与前:num=-16
与后num=-32
count=4
与前:num=-32
与后num=-64
count=5
与前:num=-64
与后num=-128
count=6
与前:num=-128
与后num=-256
count=7
与前:num=-256
与后num=-512
count=8
与前:num=-512
与后num=-1024
count=9
与前:num=-1024
与后num=-2048
count=10
与前:num=-2048
与后num=-4096
count=11
与前:num=-4096
与后num=-8192
count=12
与前:num=-8192
与后num=-16384
count=13
与前:num=-16384
与后num=-32768
count=14
与前:num=-32768
与后num=-65536
count=15
与前:num=-65536
与后num=-131072
count=16
与前:num=-131072
与后num=-262144
count=17
与前:num=-262144
与后num=-524288
count=18
与前:num=-524288
与后num=-1048576
count=19
与前:num=-1048576
与后num=-2097152
count=20
与前:num=-2097152
与后num=-4194304
count=21
与前:num=-4194304
与后num=-8388608
count=22
与前:num=-8388608
与后num=-16777216
count=23
与前:num=-16777216
与后num=-33554432
count=24
与前:num=-33554432
与后num=-67108864
count=25
与前:num=-67108864
与后num=-134217728
count=26
与前:num=-134217728
与后num=-268435456
count=27
与前:num=-268435456
与后num=-536870912
count=28
与前:num=-536870912
与后num=-1073741824
count=29
与前:num=-1073741824
与后num=-2147483648
count=30
与前:num=-2147483648
与后num=0
count=31
31

扩展1:用一条语句判断一个整数是否是2的整数次方

将2的幂次方写成二进制形式后,很容易就会发现有一个特色:二进制中只有一个1,而且1后面跟了n个0; 所以问题能够转化为判断1后面是否跟了n个0就能够了。

若是将这个数减去1后会发现,仅有的那个1会变为0,而原来的那n个0会变为1;所以将原来的数与去减去1后的数字进行与运算后会发现为零。

最快速的方法:

 

(number & number - 1) == 0

扩展2:

输 入两个整数m和n,计算须要改变m的二进制表示中的多少位才能获得n。好比10的二进制表示为1010,13的二进制表示为1101,须要改变1010中 的3位才能获得1101 。 咱们能够分为两步解决这个问题:第一步求这两个数的异或,第二步统计异或结果中1的位数。

  1. int GetCount(int N,int M)  
  2. {  
  3.     int value=N^M;//先将两个数异或  //0111=7,0110=6
  4.     int count=0;  
  5.     while(value)  
  6.     {  
  7.         count++;  
  8.         value=(value&(value-1));//求异或后1的个数  ,0111& 0110 =0110=6;//0101=5,0100=4,
  9.     }  
  10.     return count;  
相关文章
相关标签/搜索