zoukankan      html  css  js  c++  java
  • 经典面试题(1):统计整数中1的个数

    题目:给定一个无符号32位整数num,求num的二进制表示法中含1的个数?

    算法一:

        也是最直接的算法,我们先判断整数的最右边一位是不是1,接着把整数右移一位,原来处于右边第二位的数字现在被移到第一位了,再判断是不是1。这样每次移动一位,直到这个整数变成0为止。

    typedef unsigned __int64 uint64;  // 64位
    
    int count1(uint64 num) 
    { 
        int count=0; 
        while(num){ 
            count += num & 1; 
            num >>= 1; 
        } 
        return count; 
    }

    算法的复杂度为O(logn)。上面这个代码对于正整数没有问题,但是对于负数可能就会出现问题了,主要原因是循环移位造成的,现做如下改进:

    int count2(uint64 num) 
    { 
        int count=0; 
        uint64 flag = 1; 
        while(flag){ 
            if(flag & num) 
            { 
                count++; 
            } 
            flag = flag << 1; 
        } 
        return count; 
    } 

    算法二:模式位法

    如果已知大多数数据位是 0 的话,那么还有更快的算法。这些更快的算法是基于这样一种事实即 n 与 n-1 相与(&)得到的最低位永远是 0。例如:

    image

    n = n & (n -1);考虑 n 与 n -1的二进制表示,两者相&,n-1总能将n的最低位的1,置0。

    int count4(uint64 num) 
    { 
        int n = 0; 
        while(num) 
        { 
            num &= (num-1); 
            n++; 
        } 
        return n; 
    }

    维基里面给出了一种奇葩的算法,其实思想和上面的循环是一样的,只是他使用宏函数写的,乍看起来有些让人摸不着头脑,自习分析一下还是蛮简单的:

    // 如果大多数位置为0则使用下面的算法比较好. 
    // 这里使用一种利用宏的技巧,而不是使用循环,其实原理和使用循环是一样的。 
    // 因为最多有64个1,因此只要把(64+1)中情况全部写出来就行了 
    #define f(y) if ((x &= x-1) == 0) return y; 
    // 如果是1比较多可以把宏定义成这样 
    // #define f(y) if ((x |= x+1) == hff) return 64-y; 
    int count3(uint64 x) { 
        if (x == 0) return 0; 
        f( 1) f( 2) f( 3) f( 4) f( 5) f( 6) f( 7) f( 8) 
        f( 9) f(10) f(11) f(12) f(13) f(14) f(15) f(16) 
        f(17) f(18) f(19) f(20) f(21) f(22) f(23) f(24) 
        f(25) f(26) f(27) f(28) f(29) f(30) f(31) f(32) 
        f(33) f(34) f(35) f(36) f(37) f(38) f(39) f(40) 
        f(41) f(42) f(43) f(44) f(45) f(46) f(47) f(48) 
        f(49) f(50) f(51) f(52) f(53) f(54) f(55) f(56) 
        f(57) f(58) f(59) f(60) f(61) f(62) f(63) 
        return 64; 
    }

    过程比较简单,就是循环将末尾的1变成0,算法的复杂度就是1的个数。

    image

    算法三:分而治之法

    分析,n的二进制表示中位为1的位中1,还可以表示该位的1的个数为1。基于这个计数的性质。

    要求一个n位的整数的二进制的表示中1的个数:

       (1) 若n为1,返回该位的值;即该位上1的个数;

       (2) 若n>1时,等于其前n/2位中1的个数+后n/2位中1的个数;

    下面是基于这种思想的递归实现:

    int count5(uint64 num, int bitSize); 
    int count5(uint64 num) 
    { 
        return count5(num, sizeof(num) * 8); 
    } 
    int count5(uint64 num, int bitSize) 
    { 
        if(bitSize == 1) 
        { 
            return num; 
        } 
        else 
        { 
            int shiftLen = bitSize >> 1;    // 移位数 
            int numR = num >> shiftLen;        // 取高shiftLen位数字 
            int numL = num & (0xffffffffffffffff >> (sizeof(num) * 8-shiftLen));// 取低shiftLen位数字 
            return count5(numR, shiftLen) + count5( numL, shiftLen);// 合并 
        } 
    }

    算法四:汉明重量

    本算法参照了维基百科:

    image

    声明一些常量

    const uint64 m1  = 0x5555555555555555; //binary: 0101... 
    const uint64 m2  = 0x3333333333333333; //binary: 00110011.. 
    const uint64 m4  = 0x0f0f0f0f0f0f0f0f; //binary:  4 zeros,  4 ones ... 
    const uint64 m8  = 0x00ff00ff00ff00ff; //binary:  8 zeros,  8 ones ... 
    const uint64 m16 = 0x0000ffff0000ffff; //binary: 16 zeros, 16 ones ... 
    const uint64 m32 = 0x00000000ffffffff; //binary: 32 zeros, 32 ones ... 
    const uint64 hff = 0xffffffffffffffff; //binary: all ones 
    const uint64 h01 = 0x0101010101010101; //the sum of 256 to the power of 0,1,2,3... 

    ①实现方式1:

    int count6(uint64 x) { 
        x = (x & m1 ) + ((x >>  1) & m1 ); //put count of each  2 bits into those  2 bits 
        x = (x & m2 ) + ((x >>  2) & m2 ); //put count of each  4 bits into those  4 bits 
        x = (x & m4 ) + ((x >>  4) & m4 ); //put count of each  8 bits into those  8 bits 
        x = (x & m8 ) + ((x >>  8) & m8 ); //put count of each 16 bits into those 16 bits 
        x = (x & m16) + ((x >> 16) & m16); //put count of each 32 bits into those 32 bits 
        x = (x & m32) + ((x >> 32) & m32); //put count of each 64 bits into those 64 bits 
        return x; 
    } 

    ②实现方式2:

    //This uses fewer arithmetic operations than any other known  
    //implementation on machines with slow multiplication. 
    //It uses 17 arithmetic operations. 
    int count7(uint64 x) { 
        x -= (x >> 1) & m1;             //put count of each 2 bits into those 2 bits 
        x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits 
        x = (x + (x >> 4)) & m4;        //put count of each 8 bits into those 8 bits 
        x += x >>  8;  //put count of each 16 bits into their lowest 8 bits 
        x += x >> 16;  //put count of each 32 bits into their lowest 8 bits 
        x += x >> 32;  //put count of each 64 bits into their lowest 8 bits 
        return x &0xff; 
    } 

    ③实现方式3:

    //This uses fewer arithmetic operations than any other known  
    //implementation on machines with fast multiplication. 
    //It uses 12 arithmetic operations, one of which is a multiply. 
    int count8(uint64 x) { 
        x -= (x >> 1) & m1;             //put count of each 2 bits into those 2 bits 
        x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits 
        x = (x + (x >> 4)) & m4;        //put count of each 8 bits into those 8 bits 
        return (x * h01)>>56;  //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ... 
    }

    完整代码:

    #include <iostream>
    using namespace std;
    typedef unsigned __int64 uint64;  // 64位
    /* 
     * 统计一个整数中1的个数
     * 作者:樊列龙
     * from:
     * 时间:2014-01-15
     */
    
    int count1(uint64 num) 
    { 
        int count=0; 
        while(num){ 
            count += num & 1; 
            num >>= 1; 
        } 
        return count; 
    }
    
    
    int count2(uint64 num) 
    { 
        int count=0; 
        uint64 flag = 1;
        while(flag){ 
            if(flag & num) 
            {
                count++;
            }
            flag = flag << 1;
        } 
        return count; 
    }
    
    
    // 如果大多数位置为0则使用下面的算法比较好.
    // 这里使用一种利用宏的技巧,而不是使用循环,其实原理和使用循环是一样的。
    // 因为最多有64个1,因此只要把(64+1)中情况全部写出来就行了
    #define f(y) if ((x &= x-1) == 0) return y;
    // 如果是1比较多可以把宏定义成这样
    // #define f(y) if ((x |= x+1) == hff) return 64-y;
    int count3(uint64 x) {
        if (x == 0) return 0;
        f( 1) f( 2) f( 3) f( 4) f( 5) f( 6) f( 7) f( 8)
        f( 9) f(10) f(11) f(12) f(13) f(14) f(15) f(16)
        f(17) f(18) f(19) f(20) f(21) f(22) f(23) f(24)
        f(25) f(26) f(27) f(28) f(29) f(30) f(31) f(32)
        f(33) f(34) f(35) f(36) f(37) f(38) f(39) f(40)
        f(41) f(42) f(43) f(44) f(45) f(46) f(47) f(48)
        f(49) f(50) f(51) f(52) f(53) f(54) f(55) f(56)
        f(57) f(58) f(59) f(60) f(61) f(62) f(63)
        return 64;
    }
    
    
    int count4(uint64 num)
    {
        int n = 0;
        while(num)
        {
            num &= (num-1);
            n++;
        }
        return n;
    }
    
    ///======================================================
    int count5(uint64 num, int bitSize);
    int count5(uint64 num)
    {
        return count5(num, sizeof(num) * 8);
    }
    int count5(uint64 num, int bitSize)
    {
        if(bitSize == 1) 
        {
            return num;
        } 
        else 
        {
            int shiftLen = bitSize >> 1;    // 移位数
            int numR = num >> shiftLen;        // 取高shiftLen位数字
            int numL = num & (0xffffffffffffffff >> (sizeof(num) * 8-shiftLen));// 取低shiftLen位数字
            return count5(numR, shiftLen) + count5( numL, shiftLen);// 合并
        }
    }
    ///======================================================
    
    const uint64 m1  = 0x5555555555555555; //binary: 0101... 
    const uint64 m2  = 0x3333333333333333; //binary: 00110011.. 
    const uint64 m4  = 0x0f0f0f0f0f0f0f0f; //binary:  4 zeros,  4 ones ... 
    const uint64 m8  = 0x00ff00ff00ff00ff; //binary:  8 zeros,  8 ones ... 
    const uint64 m16 = 0x0000ffff0000ffff; //binary: 16 zeros, 16 ones ... 
    const uint64 m32 = 0x00000000ffffffff; //binary: 32 zeros, 32 ones ... 
    const uint64 hff = 0xffffffffffffffff; //binary: all ones 
    const uint64 h01 = 0x0101010101010101; //the sum of 256 to the power of 0,1,2,3... 
      
    int count6(uint64 x) { 
        x = (x & m1 ) + ((x >>  1) & m1 ); //put count of each  2 bits into those  2 bits 
        x = (x & m2 ) + ((x >>  2) & m2 ); //put count of each  4 bits into those  4 bits 
        x = (x & m4 ) + ((x >>  4) & m4 ); //put count of each  8 bits into those  8 bits 
        x = (x & m8 ) + ((x >>  8) & m8 ); //put count of each 16 bits into those 16 bits 
        x = (x & m16) + ((x >> 16) & m16); //put count of each 32 bits into those 32 bits 
        x = (x & m32) + ((x >> 32) & m32); //put count of each 64 bits into those 64 bits 
        return x; 
    } 
      
    //This uses fewer arithmetic operations than any other known  
    //implementation on machines with slow multiplication. 
    //It uses 17 arithmetic operations. 
    int count7(uint64 x) { 
        x -= (x >> 1) & m1;             //put count of each 2 bits into those 2 bits 
        x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits 
        x = (x + (x >> 4)) & m4;        //put count of each 8 bits into those 8 bits 
        x += x >>  8;  //put count of each 16 bits into their lowest 8 bits 
        x += x >> 16;  //put count of each 32 bits into their lowest 8 bits 
        x += x >> 32;  //put count of each 64 bits into their lowest 8 bits 
        return x &0xff; 
    } 
      
    //This uses fewer arithmetic operations than any other known  
    //implementation on machines with fast multiplication. 
    //It uses 12 arithmetic operations, one of which is a multiply. 
    int count8(uint64 x) { 
        x -= (x >> 1) & m1;             //put count of each 2 bits into those 2 bits 
        x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits 
        x = (x + (x >> 4)) & m4;        //put count of each 8 bits into those 8 bits 
        return (x * h01)>>56;  //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ... 
    }
    
     
    int main()
    {
        cout << count1(27834) << endl;
        cout << count2(27834) << endl;
        cout << count3(27834) << endl;
        cout << count4(27834) << endl;
        cout << count5(27834) << endl;
        cout << count6(27834) << endl;
        cout << count7(27834) << endl;
        cout << count8(27834) << endl;
        cin.get();
    }
    View Code

    源代码下载:http://download.csdn.net/detail/csulennon/8362453

    本文参考了:

    1、http://www.xuebuyuan.com/2039902.html

    2、http://zh.wikipedia.org/wiki/%E6%B1%89%E6%98%8E%E9%87%8D%E9%87%8F

    3、http://www.cnblogs.com/xianghang123/archive/2011/08/24/2152408.html

  • 相关阅读:
    SQL 脚本 重复执行 约束
    xiami 精选集
    PHP 5 环境配置
    Thread线程类
    创建线程
    C#中简单的正则表达式(也经常会用到的)
    线程的挂起与恢复
    C#操作INI文件
    多线程简介
    单线程简介
  • 原文地址:https://www.cnblogs.com/csulennon/p/4224859.html
Copyright © 2011-2022 走看看