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