【一刷《剑指Offer》】面试题 10:二进制中 1 的个数

简介: 【一刷《剑指Offer》】面试题 10:二进制中 1 的个数

力扣对应题目链接:191. 位1的个数 - 力扣(LeetCode)

牛客对应题目链接:二进制中1的个数_牛客题霸_牛客网


一、《剑指Offer》内容

核心考点 :二进制计算。


二、分析问题

1、循环检查二进制位

可以直接循环检查给定数字 n 的二进制的每一位是否为 1,那应该如何遍历二进制每一位呢?

可以考虑移位运算,每次移动一位即可。至于怎么统计到 1 呢?

我们都只知道数字 1 与数字相位与运算,其实只是最后一位为 1 就是 1,最后一位为 0 就是 0,这样我们就只需要将数字 1 移位运算,就可以遍历二进制的每一位,再去做位与运算,结果为 1 的就是二进制中为 1 的。

当检查第 i 位时,我们可以让 n 与 2^i 进行与运算,当且仅当 n 的第 i 位为 1 时,运算结果不为 0。

  1. 遍历二进制的 32 位,通过移位 0~31 次实现。
  2. 将移位后的 1 与数字进行位与运算,结果为 1 就记录一次。

2、位运算优化

性质:n&(n−1),会将 n 的二进制中最低位由 1 变成 0。

观察这个运算:n & (n−1),其运算结果恰为把 n 的二进制位中的最低位的 1 变为 0 之后的结果。这样我们就可以利用这个位运算的性质加速我们的检查过程,我们不断地让当前的 n 与 n−1 做与运算,直到 n 的二进制位全部变为 0 即可。因为每次运算会使得 n 的最低位的 1 被翻转为 0,因此运算次数就等于 n 的二进制位中 1 的个数。

  1. 使用循环检查 n 是否为 0。
  2. 不为 0 就与 n−1 做位与运算,去掉二进制最后一位的 1,并统计次数。


三、代码

1、循环检查二进制位(两种写法)

//牛客AC代码
class Solution {
public:
    int NumberOf1(int n) {
        int cnt=0;
        for(int i=0; i<32; i++)
        {
            if(((n>>i)&1)==1)
                cnt++;
        }
        return cnt;
    }
};
 
//力扣AC代码
class Solution {
public:
    int hammingWeight(int n) {
        int res=0;
        for(int i=0; i<32; i++)
        {
            if((n>>i)&1==1)
                res++;
        }
        return res;
    }
};
//牛客AC代码
class Solution {
public:
    int NumberOf1(int n) {
        int cnt=0;
        for(int i=0; i<32; i++)
        {
            if((n&(1<<i))!=0)
                cnt++;
        }
        return cnt;
    }
};
 
//力扣AC代码
class Solution {
public:
    int hammingWeight(int n) {
        int res=0;
        for(int i=0; i<32; i++)
        {
            if(n&(1<<i))
                res++;
        }
        return res;
    }
};

2、推荐:方法一(n & (n-1))

这种方法可以避免无效检测。

//牛客AC代码
class Solution {
public:
    int NumberOf1(int n) {
        int cnt = 0;
        while(n)
        {
            n &= (n-1);
            cnt++;
        }
        return cnt;
    }
};
 
//力扣AC代码
class Solution {
public:
    int hammingWeight(int n) {
        int res=0;
        while(n)
        {
            res++;
            n&=(n-1);
        }
        return res;
    }
};

四、扩展

1、力扣相关题目链接:231. 2 的幂 - 力扣(LeetCode)


(1)分析题目

一个整数如果是 2 的整数次方,那么它的二进制表示中有且只有一位是 1,而其他所有位都是 0。根据前面的分析,把这个整数减去 1 之后再和它自己做与运算,这个整数中唯一的 1 就会变成 0。


(2)代码
class Solution {
public:
    bool isPowerOfTwo(int n) {
        if(n>0 && (n&(n-1))==0) return true;
        else return false;
    }
};

2、输入两个整数 m 和 n,计算需要改变 m 的二进制表示中的多少位才能得到 n。

(1)分析题目

比如 10 的二进制表示为 1010,13 的二进制表示为 1101,需要改变 1010 中的 3 位才能得到 1101。可以分为两步来解决这个问题:

  1. 求这两个数的异或。
  2. 统计异或结果中 1 的位数。

(2)代码
public class Solution {
    public int NumberOf1(int m, int n) {
        int num = m ^ n;
        int res = 0;
        while(num)
        {
            num = num * (num-1);
            res++;
        }
        return res;
    }
}

五、举一反三

把一个整数减去 1 之后再和原来的整数做位与运算,得到的结果相当于是把整数的二进制表示中的最右边一个 1 变成 0。很多二进制的问题都可以用这个思路解决。


相关文章
|
5月前
|
安全 编译器 C++
【剑指offer】2.2编程语言(p22-p25)——面试题1:string赋值运算函数
【剑指offer】2.2编程语言(p22-p25)——面试题1:string赋值运算函数
|
8月前
【一刷《剑指Offer》】面试题 23:从上往下打印二叉树
【一刷《剑指Offer》】面试题 23:从上往下打印二叉树
|
8月前
【一刷《剑指Offer》】面试题 22:栈的压入、弹出系列
【一刷《剑指Offer》】面试题 22:栈的压入、弹出系列
|
8月前
|
算法
【一刷《剑指Offer》】面试题 21:包含 main 函数的栈
【一刷《剑指Offer》】面试题 21:包含 main 函数的栈
|
8月前
【一刷《剑指Offer》】面试题 20:顺时针打印矩阵
【一刷《剑指Offer》】面试题 20:顺时针打印矩阵
|
8月前
【一刷《剑指Offer》】面试题 19:二叉树的镜像
【一刷《剑指Offer》】面试题 19:二叉树的镜像
|
8月前
【一刷《剑指Offer》】面试题 18:树的子结构
【一刷《剑指Offer》】面试题 18:树的子结构
|
8月前
【一刷《剑指Offer》】面试题 17:合并两个排序的链表
【一刷《剑指Offer》】面试题 17:合并两个排序的链表
|
8月前
【一刷《剑指Offer》】面试题 16:反转链表
【一刷《剑指Offer》】面试题 16:反转链表
|
8月前
【一刷《剑指Offer》】面试题 15:链表中倒数第 k 个结点
【一刷《剑指Offer》】面试题 15:链表中倒数第 k 个结点