题目: 对于一个字节的无符号整数变量,求其中的二进制表示中1的个数,要求算法执行效率尽可能高效。 题目解析: 这道题同样在【剑指offer】面试题10:二进制中1的个数中出现,不过在剑指offer中没有提到无符号数,因此比该题目中多考虑一个层次。下面总结这


题目:

对于一个字节的无符号整数变量,求其中的二进制表示中1的个数,要求算法执行效率尽可能高效。


题目解析:

这道题同样在【剑指offer】面试题10:二进制中1的个数 中出现,不过在剑指offer中没有提到无符号数,因此比该题目中多考虑一个层次。下面总结这道题的几种解法。


解法一(用于该题目):

我们通常会想到除以2就是将二进制右移一位,但要判断移除的是0还是1,就要对2取余。思路很简单,通过四则运算来解答:

int Count(Byte v)
{
    int num = 0;
    while(v){
        if(v%2)
            num++;
        v /= 2;
    }
    return num;
}
登录后复制

解法二(用于该题目)

对于除以2来表示二进制右移,除法的效率比直接移位的效率低得多。碰到这样的题目,尽量用移位来代替。位运算就五种形式:与、或、异或、左移和右移。在这些范围里面找到自己想要的运算。移位时要判断移除的是0还是1,那么这里就应该通过与操作判断最后一位是否为1。

int Count2(Byte v)
{
    int num = 0;
    while(v){
        if(v & 1)
            num++;
    //也可以用 num += v&1; 来代替上面两行
        v = v >> 1;
    }
    return num;
}
登录后复制

解法三(可以用于有符号数):

如果这个题目是有符号的话,当右移的时候,会在高位补充符号位,用上面两种方法的话,会陷入死循环。如何避免?可以先判断最高位,然后遍历n-1次判断除了最高位以外的位是否为1。这样的方法比较麻烦。我们可以变通一个思路,让与的那一位1不断的左移,直到将1移到最高位。

int Count3(Byte v)
{
    int num = 0;
    int flag = 1;
    while(flag){
        if(v & flag)
            ++num;
        flag = flag << 1;
    }
    return num;
}
登录后复制

解法四(更加高效的算法):

这种方法,充分利用了二进制的相关操作,平时应该多收集这样的运算。

一个数不为0,那么二进制中肯定含1。当让这个数减去1时,最低位的1由1->0,更低位的0由0->1。当让n与n-1相与以后,n中最低位的1变成0。一次这样的操作,消去一个1,那么二进制中有多少个1,就循环多少次即可。

int Count4(Byte v)
{
    int num = 0;
    while(v){
        ++num;
        v = v & (v-1);
    }
    return num;
}
登录后复制


解法五(空间换时间方法):

其实方法四已经够好了,但是因为只涉及到八位,我们可以利用选择直接选取相应的数据。比如0x1-0x2-0x4...这些都包含1个1;0x3-0x6...包含两个1;等等。通过switch语句选择相应的结果。但是这种方法效率可能比较低,因为,当输入v为255的时候,得比较到最后才能找到相应的数据。

int Count5(Byte v)
{
    int num = 0;
    switch(v){
    case 0x0:
        num = 0;
        break;
    case 0x1:
    case 0x2:
        ....
    }

    return num;
}
登录后复制

解法六(哈希表法):

既然想到了利用空间换时间,我们干脆用个数组来表示,index为要查找的数,counttable[index]为该数据包含1的个数。








09-09 06:14