一、 i & (1<<j)
1<<j
表示二进制表示的1
(即0001
)的所有位向左平移j
个单位后的数,如j=1
,则平移后的结果是0010
,此时得到数2
。若j=3
,平移后的结果是1000
,此时得到数8
。向左平移j
位,即表示将原来的数乘上2^j
。可以类比十进制,所有位左移j
位,相当于在后面添了j
个0
,即乘上10^j
,在二进制中,即乘上2^j
。
&
在此处表示按位与,即两个二进制表示的数,在对应位置上进行取并的操作,都为1
时取1
,否则取0
。如1010
(十进制的10)和0101
(十进制的5)进行按位与操作后,得到的是0000
(十进制的0)。
i & (1<<j)
则表示 i
和 1<<j
(即2^j
) 按位与后得到的数。1<<j
的二进制表示只有第j
个位置(从右往左数,从0开始)上的数是1
,其余位置上的数是0
,i
和1<<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]
的子集,直到取到其本身。