“对于一个字节(8bit)的无符号整型变量,求其二进制表示中1的个数,要求算法的执行效率尽可能的高”
(1)第一种方法是通过对这个数字N求余数,如果求余后有余,那么表示当前位置有一个1,否则便没有。然后N=N/2。其实就是相当于从低位向高位扫描,每除以2,就是去掉低一位,若该位为1,那么n%2=1.
int Count( int n)
{
int num=0;
while(n)
{
if(v%2==1)
{
num++;
}
n=n/2;
}
return num;
}
(2)第二种方法其实思想和第一种方法差不多,只不过N=N/2 改用移位来运算:N>>=1;然后用N 同1进行“与”运算,来判断末尾是否为1,如果N的二进制形式末尾有1,则“与”结果为1,否则为0. 利用N和0x01做与操作,来判断低位是0还是1.然后把N右移1位。想法和第一钟方法是一样的,就是利用了与操作和移位来实现,效率要比方法一高一些。
int Count( int n)
{
int num=0;
while(n)
{
num+=n &0x01;
n>>=1;
}
return num;
}
(3)第三种方式,比较巧妙。方法(1)和(2)的时间复杂度都为O(log2N)(以2为底的幂运算),因为N是以2的指数速度递减的。
如何让算法的复杂度只与“1”的个数有关。算法如下:每次做n&(n-1),n二进制中1的个数就减少1个。若n&(n-1)=0,说明已经没有1了。
印象中在《程序员面试宝典》有这个思路。
int Count( int n)
{
int num=0;
while(n)
{
n &=(n-1);
num++;
}
return num;
}
(4)效率最高应该是第五种方法,呵呵,他比较变态的将“0-255”的每个数二进制形式的“1”的个数保存成了一个数组,然后直接返回个数,时间复杂度为O(1).
int table[]=
{
0,1,1,2,1,2,2,3,1,2,
2,3,2,3,3,4,1,2,2,3,
2,3,3,4,2,3,3,4,3,4,
4,5,1,2,2,3,2,3,3,4,
2,3,3,4,3,4,4,5,2,3,
3,4,3,4,4,5,3,4,4,5,
4,5,5,6,1,2,2,3,2,3,
3,4,2,3,3,4,3,4,4,5,
2,3,3,4,3,4,4,5,3,4,
4,5,4,5,5,6,2,3,3,4,
3,4,4,5,3,4,4,5,4,5,
5,6,3,4,4,5,4,5,5,6,
4,5,5,6,5,6,6,7,1,2,
2,3,2,3,3,4,2,3,3,4,
3,4,4,5,2,3,3,4,3,4,
4,5,3,4,4,5,4,5,5,6,
2,3,3,4,3,4,4,5,3,4,
4,5,4,5,5,6,3,4,4,5,
4,5,5,6,4,5,5,6,5,6,
6,7,2,3,3,4,3,4,4,5,
3,4,4,5,4,5,5,6,3,4,
4,5,4,5,5,6,4,5,5,6,
5,6,6,7,3,4,4,5,4,5,
5,6,4,5,5,6,5,6,6,7,
4,5,5,6,5,6,6,7,5,6,
6,7,6,7,7,8
};
int Count(int n)
{
return table[n];
}
问题扩展:
给定两个正整数A和B,问把A变成B需要改变多少位?也就是A和B的二进制中有多少位是不同的?
解决思路:先让A和B做异或操作(A^B),A和B不同的位通过异或操作的1,然后统计A^B中1的个数就可以了。
测试程序如下:
int main()
{
BYTE c;
int a;
while(cin>>a)
{
c=(BYTE)a; //把输入数字转为BYTE型。当然如果统计的不是一个字节的1的个数,那么不用(BYTE)a,直接调用Count() 就可以了。
cout<<Count(c)<<endl;
}
return 0;
}