231. 2 的幂
给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。
如果存在一个整数 x 使得 n == 2^x ,则认为 n 是 2 的幂次方。
示例 1:
输入:n = 1
输出:true
解释:20 = 1
示例 2:
输入:n = 16
输出:true
解释:24 = 16
示例 3:
输入:n = 3
输出:false
示例 4:
输入:n = 4
输出:true
示例 5:
输入:n = 5
输出:false
提示:
-231 <= n <= 231 - 1
进阶:你能够不使用循环/递归解决此问题吗?
题解:
这题解法很多,最直白的想法是从-31到31遍历x来判断 n == 2^x是否成立。
我们可以使用循环解决(如法一),也可以用递归(法二)。
并且由于我们要的n需要如果满足是2的幂的话,它必定会满足其32位中只有1个1。因此我们也可以遍历位,来判断给定的n的二进制位是否是只有1个1。
那么怎么可以不使用循环?
即我们可以由两个性质得到:
即如果n满足2的幂的话,会有:(n&(n-1))==0。(由于位运算的优先级,我们最好带上括号)。
证明:
若n满足,则其必为正数(因为x为整数,所以2的x次方恒大于0),其必定二进制位中只有1个1,因此对于n-1,二者取&后,其拥有的1刚好满足“错位”,所以&后为0.(见法三)
另一个性质是n&(-n) ==n。
证明:
我们知道从二进制补码的角度考虑,-n与n相比,-n是在n的基础上将符号位换位1,其他位按位取反,最后整体在加1。
我们设n原位为a100…0,(a为1前面的位),所以-n为(-a)011…1 再加1,(-a为对a取反)即为(-a)100…0,若n满足2的幂,所以a原先为0,所以-a为1,所以对n和-n取&后发现,与原n相同。而若n不满足2的幂,则其&后明显可以看出不为n。(见法四)
上述两个性质可以让我们摆脱循环枚举。
另外还有一个取巧方法,即2的幂在32位中这里最大为2的30次方(32位限制了最大数字为2的31-1),因此对于任意一个2的幂,我们都会有其应该能被2的30次方整除的情况。(见法五)
代码:
法一:循环x法
bool isPowerOfTwo(int n){
for(int i=-31;i<31;i++){
if(n==pow(2,i))
{
return 1;
}
}
return 0;
}
法二:递归x法
C:
bool res(int n,int x){
if(pow(2,x)==n){
return 1;
}
if(x==31){
return 0;
}
return res(n,x+1);
}
bool isPowerOfTwo(int n){
return res(n,-31);
}
java:
class Solution {
public boolean isPowerOfTwo(int n) {
return res(n,0);
}
public boolean res(int n,int x){
if(Math.pow(2,x)==n){
return true;
}
if(x==31){
return false;
}
return res(n,x+1);
}
}
法三:特殊性质1
bool isPowerOfTwo(int n){
if(n>0&&(n&(n-1))==0)
return 1;
return 0;
}
法四:特殊性质2
bool isPowerOfTwo(int n){
if(n>0&&(n&(-n))==n)
return 1;
return 0;
}
法五:特殊性质3
const int BIG = 1 << 30;//太大了数字,放里面会报错,只能放外面
bool isPowerOfTwo(int n) {
return n > 0 && BIG % n == 0;
}