一.两问题的转化
乍一看,上面两个问题毫不相关,但我们深入考虑一下的话,就会发现,按位异或运算符 ^ (相同为0,不同为1)能够巧妙转化上述问题,只要将两数按位异或,就能找出两数中不同位的个数,问题就转化成了1的个数问题。
二.二进制数中1的个数
这个问题有多种解法,这里举出三种。
1.模二取余法
废话不多说的,上代码
int Count(unsigned int num)
{
int count = 0;
while (num)
{
if (num % 2 == 1)//得到最低位进行检测
{
count++;
}
num = num / 2;//去掉最低位
}
return count;
}
注:上述函数接收类型必须是无符号整数。-1的补码为(ffffffff)h
可以借助十进制理解
例如:1234
1234%10得到个位数4,再/10去掉个位数,再%10得到十位数3, /10再去掉十位数.......
同理:
一个无符号二进制数x , x%2得到最低位,/2去掉最低位,再%2得到次低位.......依次进行
直到都检测完。
2.位运算
int Count2(unsigned int num)
{
int count=0;
while (num)
{
if ((num & 1) == 1)
{
count++;
}
num = num >> 1;
}
return count;
}
注:上述函数接收类型必须是无符号整数。
有符号整数和无符号整数的右移运算是不一样的,无符号整数右移等同于逻辑右移,最高位补0,有符号整数右移最高位补符号位(正数补0,负数补1)。
如果接受对象是有符号整数,输入负数就会陷入死循环。最高位一直补1,无法退出while循环。
依次检验最低位是否为1,满足则加一,这种方法不够高效,需要每位都进行检验。
3.优化后的位运算
int Count3(int num)
{
int count = 0;
while (num)
{
count++;
num = num & (num - 1);
}
return count;
}
注:这种方法不涉及移位运算,对函数接收值类型是否有无符号不要求。
假设一个二进制数为 00110000
00110000 - 1
=00101111 &00110000 = 00100000
00100000 -1
=00011111 &00100000 = 00000000
退出循环
我们发现,每次执行完num=num&(num-1)语句后,最后面的1都会消去。实际上这种方法就是每次都消去末尾的1直到全为0,好处是执行次数少,有几个1执行几次。
以上是我的个人理解,如有错误,欢迎指出。