问题是这样的, 对于一个字节(8bit)的无符号整型变量,求其二进制表示中“1”的个数
除2余1
一个8位整型,除了末位,其他均是2的整次幂。如果模2后余1,那么必然末位是1,个数+1,否则末位为0。然后**/2**,进行一次降幂,高位就会落至低一位,去检查下一位,直到整个数为0。
public int count1(int n) {
int num = 0;
while (n > 0) {
if (n % 2 == 1)//模1相加
num++;
n /= 2;
}
return num;
}
与1位运算
同样是每次去看末位是否为1。将整数与00000001进行与操作(&),因为前7位为0,所以结果的前7为必为0;末位若为1,则结果末位为1,整个得数为1,num+1,末位为0则答案为0,num+0。此时末位检测完毕无用,右移一位,重复上述过程,直到为0。
public int count2(int n) {
int num = 0;
while (n > 0) {
num += (n&1);//讲位运算的结果加上 1 or 0
n = n >> 1;
}
return num;
}
高效位操作
我们发现,前面两种操作,对于一些数,比如10000000,就会浪费大量时间在无用的0上,那么能不能让算法的复杂度只与“1”的个数有关呢?
将n与n-1进行与操作&,可以瞬间判断从末位开始到最近的“1”,这么多位,然后将结果赋值给n。这样就不用一个一个判断末位了。
每进行一次这个操作,就说明n中有一个“1”。
因为n必定要从最近的“1”中借位,才能满足-1操作,否则-1后该数小于0。减1后,所借之位前面不变,自身为0,后面必定是从0全变为1。
然后进行与操作并赋值,就可以将第一个1抹除,num+1。之后重复上述过程,直到n等于0。
public static int count3(int n) {
int num = 0;
while (n > 0) {
n = n & (n - 1);
num++;
}
return num;
}
还有一些分支法,查表法,就是提前把所有情况写出来的一种暴力方法,在此不一一列举了。
如果这篇对你有帮助请点击下方一键三连谢谢大家