问题:
求一个字节(8bit)的无符号整型变量二进制表示中“1”的个数。要求执行效率尽可能高。
分析与解法:
【解法一】
每次除2取余,若为奇数则累加,最终累加结果为“1”的个数。
如,10100010除以2,商为1010001,余数为0;1010001除以2,商为101000,余数为1。时间复杂度O(log2v)。
代码:
int count(int v)
{
int num = 0;
while(v)
{
if(v % 2 == 1)
num++;
v /= 2;
}
return num;
}
【解法二】
将数字与00000001进行“与”操作,若结果为1,说明数字最后一位是1,累加,然后对数字进行右移。
位操作比算数运算的效率高,但时间复杂度仍为O(log2v)。
代码:
int Count(int v)
{
int num = 0;
while(v)
{
if(v & Ox01)
num++;
v >>= 1;
}
return num;
}
但是如果v为负数,因为每次右移高位补的数字是其符号位,那么最终v会变成0xFFFFFFFF而陷入死循环。
改进的代码为:
int Count(int v)
{
int num = 0;
//将无符号的0x00000001进行左移,从而避免了v右移出现死循环的情况
unsigned int flag = 1;
while(flag)
{
if(v&flag)
num++;
flag <<= 1;
}
retrun num;
}
【解法三】
v & (v -1)操作每次能消去数字二进制表示中最后一位1,该操作的次数即为数字中“1”的个数。
时间复杂度为O(m),m为v中1的个数。
代码:
int Count(int v)
{
while(v)
{
v &= (v - 1);
num++;
}
}
【解法四】
因为数字只有8位,可以使用空间换时间。查表法,类似map的思想,即将每个数字中1的个数记录在数组中,v作为数组的下标,每次只要直接查找arr[v]的值,时间复杂度为O(1)。
扩展问题:
- 如果变量是32位DWORD,使用或改进上述哪个算法?
- 求整数A与B二进制表示的汉明距离。
分析与解法:
【问题1】
由于数字长度较大,不能使用解法四,可以使用解法三或改进解法二。
【问题2】
可以先将A与B异或,得到的数值再求其中“1”的个数。