算法笔记:二进制数有多少个1

32位的整数的二进制数表示有多少个1?
一开始最容易想到的方法就是与2求余在除以2。这么一个循环来解决问题
所以一开始就写出了以下代码

    public int countOnes(int num) {
         int n = 0;
        while(num != 0 ){
            if(num %2 == 1 ){
                n++;
            }
            num /= 2;
        }        
        return n;
    }

以上代码效率不会特别高,还能够进行改进。最简单的改进方式就为将除以2这一部分改为二进制。但是该代码还是存在这BUG的,这里所说的32位整数并没有说明是否为带符号数。可以检验,这种方法只适用于不带符号的数字。那么考虑负数的话,如果余数为-1.也应该在计数的时候加上。于是就有下面代码

    public int countOnes(int num) {
         int n = 0;
        while(num != 0 ){
            //这里为32位,所以和00000001做与操作
            n += num & 0X00000001;
            num >>>= 1;
        }
        return n;
    }

这里有一个小点注意,因为这里使用的是java。所以对于带符号数,应该使用>>>的移位符号。而>>用于的是不带符号数。这样的话仅仅是对于第一种方式的补充还有改进
那么有没有存在更高效的算法呢?当然存在的。
相比与余操作,位操作的效率要高很多。时间复杂度为O(log2N),其中N为二进制的位数
那么我们就可以想能不能使用算法只判断“1”就好了呢?。于是如果希望结果为0的话,那么该数与比他小1的数做与操作,那么就可以得到0.
举个例子,比如1001,先有1001&1000 = 1000
再有1000&0111 = 0000
那么就会有下面解法

    public static int countOnes(int num) {
         int n = 0;
        for(;num != 0;n++){
            num &= (num-1);
        }
        return n;
    }

那么这一种算法会不会像第一种算法那样对于符号数是行不通的呢?其实经过测试的时候发现这种方法适用于任何情况。因为无论是带符号数还是不带符号数,在计算机中都是以二进制来存储的。那么直接进行二进制运算,当然是不存在任何问题的。这种算法的时间复杂度为O(M),M为1的个数。
当然是否存在着更快的算法呢?我们时常会使用列表的方法,以达到空间换时间的目的。所以列表法的速度是非常快的,达到O(1)。不过32位的数的话,这表有点长啊~

你可能感兴趣的:(算法笔记)