前言
来写个简单的题目。
位运算
题目
Acceptance : 34.11% Difficulty : Easy
Given an integer (singed 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 ?
Tags : Bit Manipulation
题解
题目很简单。如果用循环或者递归的话,只需要看它不断地除以4,最终会不会等于1,且全过程都能被整除。
找找规律的话,4, 16, 64, 256,看到这些4的倍数的数字的二进制数都只有一个1。这是有道理的,是4的power,则必是2的power。则必只有一个1。
再联想到之前的一个计算二进制数中1的个数(汉明码问题)的题目,那么用
while(num)
{
num &= (num-1);
cnt++;
}
就能计算出num中1的个数cnt。然而,有必要吗?
当然没有。我们只需要判断 if (num & (num -1))即可。若为真,则说明num中必含一个以上的1。若为0,则它只含有一个1。
再找找规律,拿calc.exe计算一下4 + 16 + 64 +…. 并使之处于程序员模式下的十六进制模式下。我们会发现,每次加法运算的结果的二进制数都是01010101这样婶儿的。那么,我们是不是可以用0x5555 5554来作为mask判断那个仅有的1Bit是不是在合适的位置上。
当然,这也是有道理的。2的power都只有一个1。且随指数增长,1在二进制数中依次左移1位。那么4的power应是依次左移2位。就会出现100,10 000, 10 00 000这样的power of 4。那么他们的和自然就是0101010101这样婶儿的了。
于是便有了非常简单的代码。然而Submit后WA,0没有被考虑到。修改后再Submit,WA,1也没有被考虑到。
后来把1考虑进入规律中,即成了1 + 4 + 16 + 64….,mask 可以用 0x5555 5555。
于是有了3个if语句4ms的Solution
class Solution {
public:
bool isPowerOfFour(int num) {
int mask = 0x55555555;
if (num == 0)
return false;
if (num & (num - 1))
return false;
if (num & (~mask))
return false;
else
return true;
}
};
还可以再省略下吗?
看了眼Discussion中的大牛的代码。于是便有了下面的代码:
class Solution {
public:
bool isPowerOfFour(int num) {
if (num & (num - 1))
return false;
if (num & 0x55555555)
return true;
return false;
}
};
然而这代码需要8ms来完成。
WHY!!!!
比起4ms的代码,这段代码只有精简,没有啰嗦啊。唯一一处就是当test case = 0的时候,这套代码需要运行一次减法,两次与。两个if。而4ms的代码只需要一次if就return了。
但是,当test cases != 0的时候,8ms的代码会少一次if判断啊。而且,使用0x5555 5555 直接&,而不是放入变量中(或者还有取非操作),应该会使编译出来的机器码更少更快速吧。
然而结果并非如此呢?这是为什么?