一、 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]
的子集,直到取到其本身。