剑指offer(C++)-JZ15:二进制中1的个数(算法-位运算)

作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

题目描述:

输入一个整数 n ,输出该数32位二进制表示中1的个数。其中负数用补码表示。

数据范围:−2^31<=n<=2^31−1

即范围为:−2147483648<=n<=2147483647

示例:

输入:

10

返回值:

2

说明:

十进制中10的32位二进制表示为0000 0000 0000 0000 0000 0000 0000 1010,其中有两个1。

解题思路:

本题考察位运算。三种解题思路。

1)循环按位比较

       暴力循环,对每个位的1进行统计即可。

2)位运算公式n&(n-1)

       位运算公式n&(n-1),可以将数字最右边的1变为0。比如1011,减一后为1010,与运算得到1010;又比如1010,减一后为1001,与运算得到1000。

       利用该性质进行循环,当n为0后,退出循环停止计数即可。

3)分治法

       以32位1的数字A为例,1111 1111 1111 1111 1111 1111 1111 1111。采用分而治之的思想解题,下面将一步步为大家展示分治法的流程。

3.1)分16组

       32位每两位为一组,设定16组01,再来16组10。

       0x55555555 = 0101 0101 0101 0101  0101 0101 0101 0101

       0xaaaaaaaa = 1010 1010 1010 1010  1010 1010 1010 1010

       与数字A进行与运算可以得到每组中1的个数,比如11和01得到01,11和10得到10,然后10右移1变为01,01+01得到10也就是2,即11中1的个数。

       经过上述计算后,数字A变为A1:1010 1010 1010 1010  1010 1010 1010 1010。

3.2)分8组

       32位每四位为一组,设定8组0011,再来8组1100。

       0x33333333 = 0011 0011 0011 0011  0011 0011 0011 0011

       0xcccccccc = 1100 1100 1100 1100  1100 1100 1100 1100

       与数字A1进行与运算可以得到每组中1的个数,比如1010和0011得到0010,1010和1100得到1000,然后1000右移2变为0010,0010+0010得到0100也就是4,即1111中1的个数。聪明的同学到这里已经发现规律了,可以认为是两组两位数合为一组四位数。

       经过上述计算后,数字A1变为A2:0100 0100 0100 0100  0100 0100 0100 0100。

3.3)分4组

       32位每八位为一组,设定4组0000 1111,再来4组1111 0000。

       0x0f0f0f0f = 0000 1111 0000 1111  0000 1111 0000 1111

       0xf0f0f0f0 = 1111 0000 1111 0000  1111 0000 1111 0000

       与数字A2进行与运算可以得到每组中1的个数,比如0100 0100和0000 1111得到0000 0100,0100 0100和1111 0000得到0100 0000,然后0100 0000右移4变为0000 0100,0000 0100+0000 0100得到0000 1000也就是8,即1111 1111中1的个数。每组数最后的结果其实就是该组数里1的个数。

       经过上述计算后,数字A2变为A3:0000 1000 0000 1000  0000 1000 0000 1000。

3.4)分2组

       32位每十六位为一组,设定2组0000 0000 1111 1111,再来2组1111 1111 0000 0000。

       0x00ff00ff = 0000 0000 1111 1111  0000 0000 1111 1111

       0xff00ff00 = 1111 1111 0000 0000  1111 1111 0000 0000

       与数字A3进行与运算可以得到每组中1的个数,比如0000 1000 0000 1000和0000 0000 1111 1111得到0000 0000 0000 1000,0000 1000 0000 1000和1111 1111 0000 0000得到0000 1000 0000 0000,然后0000 1000 0000 0000右移8变为0000 0000 0000 1000,0000 0000 0000 1000+0000 0000 0000 1000得到0000 0000 0001 0000也就是16,即1111 1111 1111 1111中1的个数。

       经过上述计算后,数字A3变为A4:0000 0000 0001 0000  0000 0000 0001 0000。

3.5)分1组

       32位为一组。

       0x0000ffff = 0000 0000 0000 0000  1111 1111 1111 1111

       0xffff0000 = 1111 1111 1111 1111  0000 0000 0000 0000

       与数字A4进行与运算可以得到每组中1的个数,就是0000 0000 0001 0000加0000 0000 0001 0000等于0000 0000 0010 0000,也就是32。

       之所以用这个数字A,是因为这个数字能很直观地get到这个方法的逻辑。

       经过上述计算后,数字A4变为A5:0000 0000 0000 0000  0000 0000 0010 0000。也就是最终结果。

测试代码:

1)循环按位比较

class Solution {
public:
    // 1的个数
    int  NumberOf1(int n) {
        int count = 0;
        //遍历32位
        for(int i = 0; i < 32; ++i){
            //按位比较
            if((n & (1 << i)) != 0)   
                count++;
        }
        return count;
     }
};

2)位运算公式n&(n-1)

class Solution {
public:
    // 1的个数
    int NumberOf1(int n) {
        int count = 0;
        // 当n为0时停止比较
        while(n){  
            n &= n - 1;
            count++;
        }
        return count;
     }
};

3)分治法

class Solution {
public:
    // 1的个数
    int NumberOf1(int n) {
        int temp = n;
 
        // 0x55555555 = 0101 0101 0101 0101  0101 0101 0101 0101
        // 0xaaaaaaaa = 1010 1010 1010 1010  1010 1010 1010 1010
        temp = (temp & 0x55555555) + ((temp & 0xaaaaaaaa) >> 1);
 
        // 0x33333333 = 0011 0011 0011 0011  0011 0011 0011 0011
        // 0xcccccccc = 1100 1100 1100 1100  1100 1100 1100 1100
        temp = (temp & 0x33333333) + ((temp & 0xcccccccc) >> 2);
 
        // 0x0f0f0f0f = 0000 1111 0000 1111  0000 1111 0000 1111
        // 0xf0f0f0f0 = 1111 0000 1111 0000  1111 0000 1111 0000
        temp = (temp & 0x0f0f0f0f) + ((temp & 0xf0f0f0f0) >> 4);
 
        // 0x00ff00ff = 0000 0000 1111 1111  0000 0000 1111 1111
        // 0xff00ff00 = 1111 1111 0000 0000  1111 1111 0000 0000
        temp = (temp & 0x00ff00ff) + ((temp & 0xff00ff00) >> 8);
 
        // 0x0000ffff = 0000 0000 0000 0000  1111 1111 1111 1111
        // 0xffff0000 = 1111 1111 1111 1111  0000 0000 0000 0000
        temp = (temp & 0x0000ffff) + ((temp & 0xffff0000) >> 16);
        return temp;
     }
};

你可能感兴趣的:(剑指offer,算法,c++)