经典面试题(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

posted @ 2015-01-14 21:23  csulennon  阅读(3229)  评论(0编辑  收藏  举报