判断2的指数
js
function isPowerOf2(x) {
return (x & (x - 1)) === 0
}
这个函数 isPowerOf2
用于判断一个给定的数字 x
是否是 2 的幂次方。以下是它的工作原理:
解释:
2的幂次方的特性:
- 2的幂次方的数在二进制表示中,只有一个
1
,其余位都是0
。 - 例如:
1
(2⁰)→0001
2
(2¹)→0010
4
(2²)→0100
8
(2³)→1000
- 2的幂次方的数在二进制表示中,只有一个
x - 1
的作用:- 如果
x
是 2 的幂次方,那么x - 1
会把x
的二进制中唯一的1
变成0
,并且后面的所有位变成1
。 - 例如:
4
(0100
)→3
(0011
)8
(1000
)→7
(0111
)
- 如果
x & (x - 1)
的作用:- 如果
x
是 2 的幂次方,那么x & (x - 1)
的结果一定是0
,因为x
和x - 1
的所有位都不同。 - 例如:
4 & 3
→0100 & 0011 = 0000
(0
)8 & 7
→1000 & 0111 = 0000
(0
)
- 如果
特殊情况
x = 0
:- 当
x = 0
时,0 & (0 - 1) = 0 & (-1) = 0
,但0
不是 2 的幂次方,因此需要额外判断x > 0
。
- 当
修正后的代码:
js
function isPowerOf2(x) {
return x > 0 && (x & (x - 1)) === 0;
}
关键点:
- 该函数可以正确判断 正整数 是否是 2 的幂次方。
- 修正后的版本排除了
x = 0
的情况,避免误判。 - 时间复杂度:
O(1)
(仅需一次位运算和比较)。
其他方法
是的,除了使用 x & (x - 1) === 0
的方法,还有其他几种方式可以判断一个数是否是 2 的幂次方。以下是几种常见的方法:
方法 1:利用 Math.log2
(对数法)
js
function isPowerOf2(x) {
return x > 0 && Number.isInteger(Math.log2(x));
}
原理:
Math.log2(x)
计算x
以 2 为底的对数。- 如果
x
是 2 的幂次方,则Math.log2(x)
应该是整数。 Number.isInteger()
检查该对数是否为整数。
缺点:
- 浮点数精度问题可能导致误判(如
Math.log2(8)
是3
,但Math.log2(536870912)
可能因精度误差变成29.000000000000004
)。
方法 2:位运算(计算 1
的个数)
js
function isPowerOf2(x) {
return x > 0 && (x.toString(2).match(/1/g) || []).length === 1;
}
原理:
- 将
x
转为二进制字符串,检查其中1
的个数是否恰好为 1。 - 例如:
4
→"100"
(1 个1
)→true
5
→"101"
(2 个1
)→false
缺点:
- 需要字符串转换,性能不如位运算高效。
方法 3:利用 x === (x & -x)
js
function isPowerOf2(x) {
return x > 0 && x === (x & -x);
}
原理:
x & -x
会保留x
的最低位的1
,其余位清零。- 如果
x
是 2 的幂次方,则x
只有一个1
,所以x & -x
仍然等于x
。 - 例如:
8
→1000
,-8
(补码)→...1000
→8 & -8 = 1000 & ...1000 = 1000
(8
)6
→0110
,-6
→...1010
→6 & -6 = 0110 & 1010 = 0010
(2
,不等于6
)
优点:
- 比
x & (x - 1)
更直接,但需要理解补码机制。
方法 4:暴力枚举(适用于小范围)
js
function isPowerOf2(x) {
if (x <= 0) return false;
while (x % 2 === 0) x /= 2;
return x === 1;
}
原理:
- 不断除以 2,直到
x
变成奇数。 - 如果最终
x === 1
,说明它是 2 的幂次方。
缺点:
- 效率较低,不推荐用于大数计算。
方法 5:查表法(适用于已知范围)
js
const powerOf2Set = new Set([1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 /* ... */]);
function isPowerOf2(x) {
return powerOf2Set.has(x);
}
适用场景:
- 如果已知
x
的范围较小(如 32 位整数),可以预计算所有可能的 2 的幂次方,存入Set
进行快速查找。
缺点:
- 占用额外内存,不适用于任意大数。
总结
方法 | 时间复杂度 | 适用场景 | 缺点 |
---|---|---|---|
x & (x - 1) === 0 | O(1) | 通用 | 需排除 x = 0 |
Math.log2 | O(1) | 数学计算 | 浮点精度问题 |
统计 1 的个数 | O(log x) | 直观 | 字符串操作较慢 |
x === (x & -x) | O(1) | 位运算优化 | 需理解补码 |
暴力枚举 | O(log x) | 教学示例 | 效率低 |
查表法 | O(1) | 固定范围 | 占用内存 |
推荐方法:
- 最优解:
x > 0 && (x & (x - 1)) === 0
(高效、简洁)。 - 替代方案:
x > 0 && x === (x & -x)
(同样高效,但可读性稍差)。