class Solution {
public:
bool is2pow(int x) {
if (x <= 0)return 0;
return (x & x - 1) == 0;
}
bool is4pow(int x) {
if (x <= 0)return 0;
return (x & x - 1 | x & 0xaaaaaaaa) == 0;
}
bool is8pow(int x) {
if (x <= 0)return 0;
return (x & x - 1 | x & 0xb6db6db6) == 0;
}
bool is16pow(int x) {
if (x <= 0)return 0;
return (x & x - 1 | x & 0xeeeeeeee) == 0;
}
};
时间复杂度
详细分析每个方法的工作原理:
-
is2pow
方法:
brian kernighan算法 -
is4pow
方法:
验证是否是4的幂,先确保x
是2的幂,然后检查1是否在奇数位上,因为4的任何幂的二进制表示将只有一个1,并且1出现在偶数位置上。(从右向左数,最右边的位是第0位)- 像
is2pow
方法一样,首先检查x
是否小于等于0。 - 接下来检查
x
是否是2的幂,同样使用(x & (x - 1)) == 0
。 - 然后,使用
(x & 0xAAAAAAAA) == 0
判断1是否在偶数位上。0xAAAAAAAA
是一个16进制常数,它的二进制表示为10101010101010101010101010101010
,其中所有1位都处于可能被判定为4的幂次的位置。
- 像
-
is8pow
方法:
检查8的幂,确保1出现在3的倍数的位置上。- 同样的,先进行2的幂检查
(x & (x - 1)) == 0
。 0xB6DB6DB6
是一个特殊的数,二进制表示为10110110110110110110110110110110,
其中,0的位置都是8的幂的二进制表达式中的‘1’可能出现的位置。
- 同样的,先进行2的幂检查
-
is16pow
方法:
同理,16的幂验证,确保1出现在4的倍数的位置上。- 类似地,它先检查
(x & (x - 1)) == 0
。 - 然后,它使用
0xEEEEEEEE
(二进制为11101110111011101110111011101110
),其中,0的位置都是16的幂的二进制表达式中的‘1’可能出现的位置。
- 类似地,它先检查
这类方法是构造掩码法,用一个特定的掩码来检查1的位置是否符合2的幂的幂的规则。这个掩码里的‘1’是用来屏蔽非2的幂的幂的位置的。
这个掩码的构造思路也十分简单,首先找到二的幂的这个数的二进制表达式,比如说32,是100000,然后-2,得到11110,最后把它反复拼接(从11110变成1111011110以此类推),并删去超过数据类型的部分。注意,拼接时一定要进行完整拼接之后超出了数据类型才能删多余的
拼接得到111,1011,1101,1110,1111,0111,1011,1101,1110这才是完整拼接之后的结果
然后再删去得到1011,1101,1110,1111,0111,1011,1101,1110
这个数的16进制表达式是0xbdef7bde
根据这个规律,可以得出一个构造掩码的函数
unsigned int mask(int x) {
x = x - 2;
int bit = log2(x) + 1;
do {
x |= x << bit;
bit <<= 1;
} while (bit < 16);
return x |= x << bit;
}
其中x为2的幂,16的含义是int类型下最高位数(32)的一半这样就可以快速得出对应的掩码了。
时间复杂度