题目描述:
Given an integer (signed 32 bits), write a function to check whether it is a power of 4.
Example:
Given num = 16, return true. Given num = 5, return false.
Follow up: Could you solve it without loops/recursion?
要完成的函数:
bool isPowerOfFour(int num)
说明:
这道题目是“2的整数次幂”的升级版本。从前文leetcode-231-Power of Two可知,这道题目我们有两种方法去处理。
1、传统方法:
先处理边界条件,如果小于等于0那么肯定不是4的整数次幂。然后,判断能不能整除4,如果可以一直除下去,直到最后等于1。如果在整除的过程中不能再整除4了,那么必定不是4的整数次幂。
代码如下:
bool isPowerOfFour(int num)
{
if(num<=0)
return false;
while(num!=1)
{
if(num%4==0)
num>>=2;
else
return false;
}
return true;//num==1和num>1的情况都可以处理
}
时间复杂度O(logn),实测6ms,beats 78.26% of cpp submissions。
2:、第一种位操作法:
首先跟2的整数次幂一样,利用(num&(num-1))==0来判断是不是2的整数次幂,对这个判断条件感兴趣的同学可以先看一下前文。
利用这个条件,其实我们可以知道符合这个条件的num都是只有1位为1,其余位为0的数。(二进制表示)
接着我们要判断出:
0000 0001 是4的整数次幂
0000 0100 是4的整数次幂
0001 0000 是4的整数次幂
0100 0000 是4的整数次幂
而
0000 0010 不是
0000 1000 不是
0010 0000 不是
1000 0000 不是
我们要做的工作其实就是检测1有没有出现在奇数位上,比如第一位,第三位,第五位,第七位……
所以我们可以使用模板:0101 0101,来跟num值相“与”,判断在奇数位上有没有出现1。(这种方法也是检测1有没有出现在某个位上的常用做法)
而把上述模板扩展到32位,就是0x55555555。
代码如下:
bool isPowerOfFour(int num)
{
return((num&(num-1))==0 && num>0 && (num&0x55555555)==num);
}
时间复杂度O(1),实测仍然是6ms……
3、第二种位操作法:
同样首先利用(num&(num-1))==0进行第一步判断。
接着我们可以利用4的整数次幂num的一个特性,就是num-1的值都是3的整数倍。
比如:
num为 1,也就是 0000 0001,num-1为0,也就是0000 0000。
num为 4,也就是0000 0100,num-1为3 ,也就是0000 0011。
num为16,也就是0001 0000,num-1为15,也就是0000 1111。
num为64,也就是0100 0000,num-1为63,也就是0011 1111。
我们发现num-1的值,都是从num-1=3那里开始,11向左移动两位,然后再加上11,形成num-1=15。
然后再左移两位,再加上11,形成num-1=63。
3的整数次倍(最开始的num-1=3)向左移动两位,再加上11(二进制),形成的数必定是3的整数次倍。(数学归纳法可证)
所以利用这个4的整数次幂拥有而二的整数次幂没有的性质,我们可以构造出如下代码:
bool isPowerOfFour(int num)
{
return (num>0 && ((num-1)&num)==0 && (num-1)%3==0;
}
时间复杂度仍然为O(1),实测依旧是6ms……
4、不能实现的一种做法:
由于4不是素数,所以前文中第三种做法——找一个最大的int型整数,接着判断能否整除num的做法,对于这道题是不可行的。
我们找到的这个最大的int型整数,即4^15,本身也可以整除2,4^15不是只含有4这个因子。