获取最右边的 1: x x x & ( − x ) (-x) (−x)。
首先讨论为什么 x x x & ( − x ) (-x) (−x)可以获取到二进制中最右边的 1,且其它位设置为 0。
在补码表示法中, − x -x −x = = = ¬ x ¬x ¬x + + + 1 1 1。换句话说,要计算 − x −x −x,则要将 x x x 所有位取反再加 1。
在二进制表示中, ¬ x ¬x ¬x + + + 1 1 1表示将该 1 移动到 ¬ x ¬x ¬x 中最右边的 0 的位置上,并将所有较低位的位设置为 0。而 ¬ x ¬x ¬x 最右边的 0 的位置对应于 x x x 最右边的 1 的位置。
总而言之, − x -x −x = = = ¬ x ¬x ¬x + + + 1 1 1,此操作将 x x x 所有位取反,但是最右边的 1 除外。
因此, x x x 和 − x −x −x 只有一个共同点:最右边的 1。这说明 x x x & ( − x ) (-x) (−x) 将保留最右边的 1。并将其他的位设置为 0。检测是否为 2 的幂:
我们通过 x x x & ( − x ) (-x) (−x) 保留了最右边的 1,并将其他位设置为 0 若 x x x 为 2 的幂,则它的二进制表示中只包含一个 1,则有 x x x & ( − x ) = x (-x) = x (−x)=x。
若 x x x 不是 2 的幂,则在二进制表示中存在其他 1,因此 x x x & ( − x ) ! = x (-x) != x (−x)!=x。
因此判断是否为 2 的幂的关键是:判断 x x x & ( − x ) = = x (-x) == x (−x)==x。
class Solution { public: bool isPowerOfTwo(int n) { if(n == 0) { return false; } long long ret = (long long)n; return (ret & (-ret)) == ret; } }; 时间复杂度: O ( 1 ) O(1) O(1)。空间复杂度: O ( 1 ) O(1) O(1)去除二进制中最右边的 1: x x x & ( x − 1 ) (x - 1) (x−1)
首先讨论为什么 x x x & ( x − 1 ) (x - 1) (x−1) 可以将最右边的 1 设置为 0。
( x − 1 ) (x - 1) (x−1) 代表了将 x x x 最右边的 1 设置为 0,并且将较低位设置为 1。
再使用与运算:则 x 最右边的 1 和就会被设置为 0,因为 1 & 0 = 0。
检测是否为 2 的幂:
2 的幂二进制表示只含有一个 1。
x x x & ( x − 1 ) (x - 1) (x−1) 操作会将 2 的幂设置为 0,因此判断是否为 2 的幂是:判断 x x x & ( x − 1 ) = = 0 (x - 1) == 0 (x−1)==0。
class Solution { public: bool isPowerOfTwo(int n) { if(n == 0) { return false; } long long ret = (long long)n; return (ret & (ret - 1)) == 0; } }; 时间复杂度: O ( 1 ) O(1) O(1)。空间复杂度: O ( 1 ) O(1) O(1)总之,这道题还是比较简单的,就是位运算的运用