402,位1的个数系列(三)
想了解更多数据结构以及算法题,可以关注微信公众号“数据结构和算法”,每天一题为你精彩解答。也可以扫描下面的二维码关注
之前我们有两篇是讲了位1的个数,没看过的可以看下,364,位1的个数系列(一),385,位1的个数系列(二)。其中上一篇我们没有使用for循环以及while循环,使用的是相加的方式,今天我们讲的是和上一题非常相似的解法,相减的方式。
每两位存储
比如我们用两位数字存储的时候,我们只需要用这两位数减去偶数位上的值(从右往左数),比如
- 二进制00:00-0=00(表示有0个1)
- 二进制01:01-0=01(表示有1个1)
- 二进制10:10-1=01(表示有1个1)
- 二进制11:11-1=10(表示有2个1)
我们发现除了第一步和《位1的个数系列(二)》不同以外,其他的我们都可以完全按照上一题的方式来写,我们还是以-3为例来画个图看一下
上面的分析如果能够看的懂的话,那么代码就容易多了,我们来看下
public static int bitCount(int i) {
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i + (i >>> 4)) & 0x0f0f0f0f;
i = i + (i >>> 8);
i = i + (i >>> 16);
return i & 0x3f;
}
具体细节就不再这里分析了,如果想了解更多可以看《位1的个数系列(二)》
每3位存储
我们两位能存储,那么3位呢,看过《位1的个数系列(二)》的都知道,这也是可以的,之前我们介绍过的是通过一个个相加,也很好理解,那么这里如果使用的是相减应该怎么计算呢,我们来画个图看一下吧
通过上面的图分析我们找到了规律,就是每3位存储的话,只需要执行a=a-(a>>>1)-(a>>>2)就可以把数字存储到这3位数中,原理很简单,我们来看下代码
public static int bitCount(int n) {
n = n - ((n >>> 1) & 033333333333) - ((n >>> 2) & 011111111111);
n = ((n + (n >>> 3)) & 030707070707);
n = ((n + (n >>> 6)) & 07700770077);
n = ((n + (n >>> 12)) & 037700007777);
return ((n + (n >>> 24))) & 63;
}
结果对不对,我们只有找几组数据经过测试之后才具有说服力,我们顺便找几组数据测试一下
int num = 100000000;
System.out.println(num + " 的二进制是----->" + Util.bitInt32(num) + "---->1的个数是:" + bitCount(num));
num = Integer.MAX_VALUE;
System.out.println(num + "的二进制是----->" + Util.bitInt32(num) + "---->1的个数是:" + bitCount(num));
num = 0;
System.out.println(num + " 的二进制是----->" + Util.bitInt32(num) + "---->1的个数是:" + bitCount(num));
num = -1;
System.out.println(num + " 的二进制是----->" + Util.bitInt32(num) + "---->1的个数是:" + bitCount(num));
num = Integer.MAX_VALUE - 10000;
System.out.println(num + " 的二进制是----->" + Util.bitInt32(num) + "---->1的个数是:" + bitCount(num));
num = Integer.MIN_VALUE;
System.out.println(num + "的二进制是----->" + Util.bitInt32(num) + "---->1的个数是:" + bitCount(num));
我们再来看一下打印结果
100000000 的二进制是----->00000101 11110101 11100001 00000000 ---->1的个数是:12
2147483647的二进制是----->01111111 11111111 11111111 11111111 ---->1的个数是:31
0 的二进制是----->00000000 00000000 00000000 00000000 ---->1的个数是:0
-1 的二进制是----->11111111 11111111 11111111 11111111 ---->1的个数是:32
2147473647 的二进制是----->01111111 11111111 11011000 11101111 ---->1的个数是:26
-2147483648的二进制是----->10000000 00000000 00000000 00000000 ---->1的个数是:1
结果丝毫不差。
每多位存储
看明白了上面每3位存储的分析过程,那么这题解法就比较多了,我们可以类推每4位存储的时候我们只需要执行a=a-(a>>>1)-(a>>>2)-(a>>>3),就可以把每4位1的个数存储在这4位数中,同理每5位存储的时候我们只需要执行a=a-(a>>>1)-(a>>>2)-(a>>>3)-(a>>>4),就可以把每5位1的个数存储在这5位数中。每6位的时候……,那么这样写下去就非常多了。我们就分别以每4位和每5位为例来写一下。首先写的是每4位为一组来存储
public static int bitCount(int n) {
int tmp = n - ((n >>> 1) & 0x77777777) - ((n >>> 2) & 0x33333333) - ((n >>> 3) & 0x11111111);
tmp = ((tmp + (tmp >>> 4)) & 0x0f0f0f0f);
tmp = ((tmp + (tmp >>> 8)) & 0x00ff00ff);
return ((tmp + (tmp >>> 16)) & 0x0000ffff) % 63;
}
再来看一个每5位为一组来存储
public static int bitCount(int n) {
n = n - ((n >>> 1) & 0xdef7bdef) - ((n >>> 2) & 0xce739ce7) - ((n >>> 3) & 0xc6318c63) - ((n >>> 4) & 0x02108421);
n = ((n + (n >>> 5)) & 0xc1f07c1f);
n = ((n + (n >>> 10) + (n >>> 20) + (n >>> 30)) & 63);
return n;
}
我们随便找几组数据测试一下
int num = 1000;
System.out.println(num + " 的二进制是----->" + Util.bitInt32(num) + "---->1的个数是:" + bitCount(num));
num = Integer.MAX_VALUE;
System.out.println(num + "的二进制是----->" + Util.bitInt32(num) + "---->1的个数是:" + bitCount(num));
num = 0;
System.out.println(num + " 的二进制是----->" + Util.bitInt32(num) + "---->1的个数是:" + bitCount(num));
num = -1;
System.out.println(num + " 的二进制是----->" + Util.bitInt32(num) + "---->1的个数是:" + bitCount(num));
num = -8;
System.out.println(num + " 的二进制是----->" + Util.bitInt32(num) + "---->1的个数是:" + bitCount(num));
num = Integer.MIN_VALUE;
System.out.println(num + "的二进制是----->" + Util.bitInt32(num) + "---->1的个数是:" + bitCount(num));
我们就用最后一个每5位为一组的来测试一下,来看下结果
1000 的二进制是----->00000000 00000000 00000011 11101000 ---->1的个数是:6
2147483647的二进制是----->01111111 11111111 11111111 11111111 ---->1的个数是:31
0 的二进制是----->00000000 00000000 00000000 00000000 ---->1的个数是:0
-1 的二进制是----->11111111 11111111 11111111 11111111 ---->1的个数是:32
-8 的二进制是----->11111111 11111111 11111111 11111000 ---->1的个数是:29
-2147483648的二进制是----->10000000 00000000 00000000 00000000 ---->1的个数是:1
结果完全正确
总结
我们通过这3个系列,算是把这道题完全讲透了,这题虽然看似简单,但其中蕴含的解题思路确是非常多的,如果这题的所有解法都看懂了,那么对二进制的掌握程度将会有一个很大的提升。