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

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

题目描述:

输入一个整数 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;
     }
};


相关文章
|
1月前
|
算法 测试技术 C++
【动态规划算法】蓝桥杯填充问题(C/C++)
【动态规划算法】蓝桥杯填充问题(C/C++)
|
1月前
|
存储 算法 C++
高精度算法(加、减、乘、除,使用c++实现)
高精度算法(加、减、乘、除,使用c++实现)
443 0
高精度算法(加、减、乘、除,使用c++实现)
|
1月前
|
算法 数据处理 C++
c++ STL划分算法;partition()、partition_copy()、stable_partition()、partition_point()详解
这些算法是C++ STL中处理和组织数据的强大工具,能够高效地实现复杂的数据处理逻辑。理解它们的差异和应用场景,将有助于编写更加高效和清晰的C++代码。
22 0
|
1月前
|
存储 算法 决策智能
【算法】博弈论(C/C++)
【算法】博弈论(C/C++)
|
1月前
|
存储 算法 C++
【算法】哈希映射(C/C++)
【算法】哈希映射(C/C++)
|
1月前
|
机器学习/深度学习 人工智能 算法
【算法】最长公共子序列(C/C++)
【算法】最长公共子序列(C/C++)
|
1月前
|
人工智能 算法 BI
一篇带你速通差分算法(C/C++)
一篇带你速通差分算法(C/C++)
|
1月前
|
人工智能 算法 C++
一篇带你速通前缀和算法(C/C++)
一篇带你速通前缀和算法(C/C++)
|
1月前
|
存储 算法 C++
弗洛伊德(Floyd)算法(C/C++)
弗洛伊德(Floyd)算法(C/C++)
|
1月前
|
存储 算法 程序员
迪杰斯特拉(Dijkstra)算法(C/C++)
迪杰斯特拉(Dijkstra)算法(C/C++)