Java中,int类型占四字节,即32位,这里我们假设正整数n是int型,那么正整数32的二进制表示为:
0000 0000 0000 0000 0000 0000 0010 0000
法一:位移法
我们对每一位进行判断,首先判断最低位,如果是1,那1的总个数加1,然后右移一位后,再判断最低位,位移32次,进行循环判断。
这种操作思路很简单,但存在一定的问题:不管n的二进制表示中含有多少个1,都需要位移32次,每次判断最低位是否为1,比较耗时。
法二:求与法
这种方法借助n&(n-1)这个操作,它可以消除n的二进制表示中的最后一个1,消除多少次,就有多少个1
法三:查表法
这种方法的思路是以空间换时间,即我先把每一个正整数对应的二进制1的个数用数组存储下来,如下:
res[1] = 1;
res[2] = 1;
res[3] = 2;
res[4] = 1;
…
…
…
res[32767] = 14;
…
…
如果要计算n的二进制表示中1的个数,直接返回res[n]即可。时间复杂度为O(1)
但这种方法有个问题:改表示结果的数组占用较大的内存空间,在内存空间有限制的情况,这种方法不可行。
接下来我们思考一下怎么减少内存空间的使用。算法的设计,本身是一个时间复杂度和空间复杂度的折衷,增加计算次数,往往能够减少存储空间。
思路:把正整数n分解为低16位正整数n1,高16位正整数n2
n1查一次表,其二进制表示包含x个1
n2查一次表,其二进制表示包含y个1
则n的二进制表示包含x+y个1