关于 i & (1<>j) 的解释

一、 i & (1<<j)

  1<<j表示二进制表示的1(即0001)的所有位向左平移j个单位后的数,如j=1,则平移后的结果是0010,此时得到数2。若j=3,平移后的结果是1000,此时得到数8向左平移j位,即表示将原来的数乘上2^j可以类比十进制,所有位左移j位,相当于在后面添了j0,即乘上10^j,在二进制中,即乘上2^j

  &在此处表示按位与,即两个二进制表示的数,在对应位置上进行取并的操作,都为1时取1,否则取0。如1010(十进制的10)和0101(十进制的5)进行按位与操作后,得到的是0000(十进制的0)。

  i & (1<<j)则表示 i1<<j(即2^j) 按位与后得到的数。1<<j的二进制表示只有第j个位置(从右往左数,从0开始)上的数是1,其余位置上的数是0i1<<j 进行按位与操作时,i的第j个位置是1就返回1<<j(判断语句中即为true),i的第j个位置是0就返回0(判断语句中即为false

ex:i=18,j=3
00010010
00001000
________
00000000

具体地,

i=0时,任何j都不满足。

i=1时,j=0 满足条件。

i=2时,j=1 满足条件。

i=3时,j=0,1 满足条件。

i=4时,j=2 满足条件。

i=5时,j=0,2 满足条件。

i=6时,j=1,2 满足条件。

i=7时,j=0,1,2 满足条件。

i=8时,j=3 满足条件。

...

i=2^n-1时,j=0,1,2,...,n-1 满足条件。

  归纳一下,如果j表示数组a(长度为n)的索引,循环结构如下,则表示依次选取数组a的一个子数组进行操作,直到选到它本身。

for (let i=1; i<1<<n; ++i) {
        for (let j=0; j<n; ++j) {
            if (i & (1<<j)) {}
        }
    }

  例子:LeetCode 2044. 统计按位或能得到最大值的子集数目(中等),代码参考文献1

var countMaxOrSubsets = function(nums) {
    let cnt=0,res=0,n=nums.length;
    for (let i=1; i<1<<n; ++i) {
        let tem=0;
        for (let j=0; j<n; ++j) {
            if (i & (1<<j)) {
                tem |= nums[j];
            }
        }
        if (tem>res) {res=tem;cnt=0;}
        if (tem == res) {cnt++;}
    }
    return cnt;
};

例如,当a=[3,1]时,

i=1,j=0时,取[3]tem=3,res=3,cnt=1

i=2,j=1时,取[1]tem=1,res=3,cnt=1

i=3,j=0时,取[3]tem=3;

i=3,j=1时,取[1]tem=3,res=3,cnt=2

返回2。

二、 1 & (i>>j)

  1 & (i>>j)相当于i 右移 j 位后,若为奇数则返回1,若为偶数则返回0。代码相当于:

let a = i/(2**j);
if (a % 2 == 0) {
	return 0;
} else {
    return 1;
}

i>=0 && i < 2^n ,j>=0 && j<n ,具体地,

i=0时,任何j都不满足。

i=1 时,j=0满足条件。

i=2 时,j=1时满足条件。

i=3 时,j=0,1时满足条件。

i=4 时,j=2时满足条件。

i=5 时,j=0,2时满足条件。

i=6 时,j=1,2时满足条件。

i=7 时,j=0,1,2时满足条件。

i=8 时,j=3时满足条件。

...

i=2^n-1 时,j=0,1,2,...,n-1时满足条件。

  归纳,1 & (i>>j)i & (1<<j) 的效果一样,都是依次取出[0,1,2,...,n-1]的子集,直到取到其本身。

参考

1.5904. js 暴力

上一篇:多校NOIP18


下一篇:物联网平台OTA固件升级使用说明