• BCZM: Chapter 2


    2.1 二进制数中 1 的个数

        实现一个函数,输入一个无符号整数,输出该数二进制中的1的个数。例如把9表示成二进制是1001,有2位是1,因此如果输入9,该函数输出2

       

        分析与解法

        解法1:利用十进制和二进制相互转化的规则,依次除余操作的结果是否为1,代码如下:

    int Count1(unsigned int v)
    {
        int num = 0;
        
        while(v)
        {
            if(1 == v % 2)
            {
                ++num;
            }
            
            v /= 2;
        }
        
        return num;
    }

        解法2:向右移位操作同样可以达到相同的目的,唯一不同的是,移位之后如何来判断是否有1存在。对于这个问题,举例:10100001,在向右移位的过程中,我们会把最后一位丢弃,因此需要判断最后一位是否为1,这个需要与00000001进行位“与”操作,看结果是否为1,如果为1,则表示当前最后八位最后一位为1,否则为0,解法代码实现如下,时间复杂度为O(log2v)。

    int Count2(unsigned int v)
    {
        unsigned int num = 0;
        
        while(v)
        {
            num += v & 0x01;
            v >>= 1;
        }
        
        return num;
    }

        解法3:利用"与"操作,不断清除n的二进制表示中最右边的1,同时累加计数器,直至n为0,这种方法速度比较快,其运算次数与输入n的大小无关,只与n中1的个数有关。如果n的二进制表示中有M个1,那么这个方法只需要循环k次即可,所以其时间复杂度O(M),代码实现如下:

    int Count3(unsigned int v)
    {
        int num = 0;
        
        while(v)
        {
            v &= (v-1);
            ++num;
        }
        
        return num;
    }

        编程之美同时给出了8bit的情况下,解法4:使用分支操作,解法5:查表法 再计算32bit无符号整数时,需要将32bit切为4部分 然后每部分分别运用解法4解法5下面仅给出代码:

        解法4:

    int Count4(unsigned int v)
    {
        int num = 0;
        
        switch(v)
        {
            case 0x0:
                num = 0;
                break;
            case 0x1:
            case 0x2:
            case 0x4:
            case 0x8:
            case 0x10:
            case 0x20:
            case 0x40:
            case 0x80:
                num = 1;
                break;
            case 0x3:
            case 0x6:
            case 0xc:
            case 0x18:
            case 0x30:
            case 0x60:
            case 0xc0:
                num = 2;
                break;
                //.....
        }
        
        return num;
    }

        解法5:

    unsigned int table[256] =
    {
        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 CountTable(unsigned int v)
    {
        return table[v & 0xff] +
        table[(v >> 8) & 0xff] +
        table[(v >> 16) & 0xff] +
        table[(v >> 24) & 0xff];
    }

        平行算法,思路:将v写成二进制形式,然后相邻位相加,重复这个过程,直到只剩下一位。以217(11011001)为例,有图有真相,下面的图足以说明一切了。217的二进制表示中有5个1。

    int Count6(unsigned int v)
    {
        v = (v & 0x55555555) + ((v >> 1) & 0x55555555);
        v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
        v = (v & 0x0f0f0f0f) + ((v >> 4) & 0x0f0f0f0f);
        v = (v & 0x00ff00ff) + ((v >> 8) & 0x00ff00ff);
        v = (v & 0x0000ffff) + ((v >> 16) & 0x0000ffff);
        
        return v;
    }

        扩展问题:求整数A和B的二进制表示中有多少位不同。

        思路:首先A与B进行异或运算,结果M,计算M中含有的1的个数。

  • 相关阅读:
    智能指针的理解
    [转] weak_ptr解决shared_ptr环状引用所引起的内存泄漏
    模板实现多态的功能
    Protobuf的自动反射消息类型的方法
    [转] C++临时变量的生命周期
    C++转换函数
    [转] boost------ref的使用(Boost程序库完全开发指南)读书笔记
    c++回调编程本质
    New 和 GetMem 的不同之处
    Delphi New,Getmem,ReallocMem联系与区别
  • 原文地址:https://www.cnblogs.com/noryes/p/8728921.html
Copyright © 2020-2023  润新知