问题描述
在程序设计中经常会遇到这样一个问题,即在一个32位整数中,从右到左寻找第一个为1的位。这样的问题是很常见的,而面对这样一个问题,一种常见的解法就是逐位的遍历这个整数中的所有位,直到遇到了为1的位。若第一个为1的位出现在高位区域,逐位查找比较的方法会比较浪费
时间。
换一种思路
当我们在一个数组中寻找某数的时候,我们有什么办法?简单的顺序的遍历整个数组的思想是可行的,这样的时间复杂度是o(n)。若数组是一个有序的数组呢?我们一般会采用2分查找的方式在一个数组中寻找某数,这样找起来就比较快了,时间复杂度是o(logn)。那在一个32位的int中寻找第一个为1的数,能否使用2分查找法呢?
在查找第一个为1的位时是可以使用2分查找法的,那么要如何使用2分法呢?关于位相关的操作一定不要忘了位运算,有效的运用位运算是可以实现对该问题的2分查找法。
究竟如何进行2分查找呢?
判断某一位是否为1时可以使用&操作,那么要判断32位中的16位是否有1时要如何做到呢?用data & 0xFFFF即可。要判断8位是否有1时可以data & 0xFF,判断4位是否有1可以data & 0xF,那么判断2位是否有1就可以用data & 0x3了。
分析到这里,就已经有思路了,通过data与 0xFFFF,0xFF,0xF以及 0x3等做&操作即可。
从左到右
int binarysearch(int data)
{
int pos=0;
if ((data & 0xFFFF0000) == 0)
pos += 16;
else
data >>= 16;
if ((data & 0xFF00) == 0)
pos += 8;
else
data >>= 8;
if ((data & 0xF0) == 0)
pos += 4;
else
data >>= 4;
if ((data & 0xC) == 0) // 0b1100
pos += 2;
else
data >>= 2;
if ((data & 0x2) == 0) // 0b10
pos += 1;
return pos;
}
从右到左
int binarysearch(int data)
{
int pos=0;
if ((data & 0x0000FFFF) == 0){
data >>= 16;
pos += 16;
}
if ((data & 0x00FF) == 0){
data >>= 8;
pos += 8;
}
if ((data & 0x0F) == 0){
data >>= 4;
pos += 4;
}
if ((data & 0x3) == 0){ // 0b1100
data >>= 2;
pos += 2;
}
if ((data & 0x1) == 0) // 0b10
pos += 1;
return pos;
}
小结
位运算在C语言中是非常重要的,很多有技巧性的优化都是通过位运算来实现,需要熟练掌握才行。
当然针对特定的处理器还可以做些指令级的优化,比如在x86上记得有一条指令就可以完成这个功能了。
(从网友博客转帖过来,原来代码有误这里做了些修改)