【leetcode热题】 位1的个数

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 '1' 的个数(也被称为汉明重量)。

提示:

  • 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
  • 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在 示例 3 中,输入表示有符号整数 -3

示例 1:

输入:n = 00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。

示例 2:

输入:n = 00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。

示例 3:

输入:n = 11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。

解法一

简单粗暴些,依次判断最低位是否是 1,然后把它加入到结果中。判断最低位是否是 1,我们只需要把原数字和 000000..001 相与,也就是和 1 相与即可。

public int hammingWeight(int n) {
    int count = 0;
    while (n != 0) {
        count += n & 1;
        n >>>= 1;
    }
    return count;
}

解法二

比较 trick 的方法,官方 题解提供的,分享一下。

有一个方法,可以把最右边的 1 置为 0,举个具体的例子。

比如十进制的 10,二进制形式是 1010,然后我们只需要把它和 9 进行按位与操作,也就是 10 & 9 = (1010) & (1001) = 1000,也就是把 1010 最右边的 1 置为 0

规律就是对于任意一个数 n,然后 n & (n-1) 的结果就是把 n 的最右边的 1 置为 0 。

也比较好理解,当我们对一个数减 1 的话,比如原来的数是 ...1010000,然后减一就会向前借位,直到遇到最右边的第一个 1,变成 ...1001111,然后我们把它和原数按位与,就会把从原数最右边 1 开始的位置全部置零了 ...10000000

有了这个技巧,我们只需要把原数依次将最右边的 1 置为 0,直到原数变成 0,记录总共操作了几次即可。

public int hammingWeight(int n) {
    int count = 0;
    while (n != 0) {
        n &= (n - 1);
        count += 1;
    }
    return count;
}

解法三

有点类似于 190 题 的解法二,通过整体的位操作解决问题,参考 这里-by-time-m-is-the-count-of-1's-and-another-several-method-of-O(1)-time) ,也是比较 trick 的,不容易想到,但还是很有意思的。

本质思想就是用本身的比特位去记录对应位数的比特位 1 的个数,举个具体的例子吧。为了简洁,求一下 8 比特的数字中 1 的个数。

统计数代表对应括号内 1 的个数
1   1   0   1   0   0   1   1
首先把它看做 8 组,统计每组 1 的个数
原数字:(1)   (1)   (0)   (1)   (0)   (0)   (1)   (1)
统计数:(1)   (1)   (0)   (1)   (0)   (0)   (1)   (1)
每个数字本身,就天然的代表了当前组 1 的个数。

接下来看做 4 组,相邻两组进行合并,统计数其实就是上边相邻组统计数相加即可。
原数字:(1    1)   (0    1)   (0   0)   (1  1)
统计数:(1    0)   (0    1)   (0   0)   (1  0)
十进制:   2           1         0         2        

接下来看做 2 组,相邻两组进行合并,统计数变成上边相邻组统计数的和。
原数字:(1    1     0    1)   (0   0     1  1)
统计数:(0    0     1    1)   (0   0     1  0)
十进制:         3                   2  

接下来看做 1 组,相邻两组进行合并,统计数变成上边相邻组统计数的和。
原数字:(1    1     0    1     0   0     1  1)
统计数:(0    0     0    0     0   1     0  1)
十进制:                   5

看一下 「统计数」的变化,也就是统计的 1 的个数。

看下二进制形式的变化,两两相加。

看下十进制形式的变化,两两相加。

最后我们就的得到了 1 的个数是 5

所以问题的关键就是怎么实现每次合并相邻统计数,我们可以通过位操作实现,举个例子。

比如上边 4 组到 2 组中的前两组合成一组的变化。要把 (1 0) (0 1) 两组相加,变成 (0 0 1 1) 。其实我们只需要把 1001 和 0011 相与得到低两位,然后把 1001 右移两位再和 0011 相与得到高两位,最后将两数相加即可。也就是(1001) & (0011) + (1001) >>> 2 & (0011)= 0011

扩展到任意情况,两组合并成一组,如果合并前每组的个数是 n,合并前的数字是 x,那么合并后的数字就是 x & (000...111...) + x >>> n & (000...111...),其中 0 和 1 的个数是 n

public int hammingWeight(int n) {
    n = (n & 0x55555555) + ((n >>> 1) & 0x55555555); // 32 组向 16 组合并,合并前每组 1 个数
    n = (n & 0x33333333) + ((n >>> 2) & 0x33333333); // 16 组向 8 组合并,合并前每组 2 个数
    n = (n & 0x0f0f0f0f) + ((n >>> 4) & 0x0f0f0f0f); // 8 组向 4 组合并,合并前每组 4 个数
    n = (n & 0x00ff00ff)+ ((n >>> 8) & 0x00ff00ff); // 4 组向 2 组合并,合并前每组 8 个数
    n = (n & 0x0000ffff) + ((n >>> 16) & 0x0000ffff); // 2 组向 1 组合并,合并前每组 16 个数
    return n;
}

写成 16 进制可能不好理解,我们拿16 组向 8 组合并举例,合并前每组 2 个数。也就是上边我们推导的,我们要把 (1 0) (0 1) 两组合并,需要和 0011 按位与,写成 16 进制就是 3,因为合并完是 8 组,所以就是 8 个 3,即 0x33333333

相关推荐

  1. 【技巧】Leetcode 191. 1个数【简单】

    2024-03-24 03:10:01       9 阅读
  2. leetcode】颠倒二进制

    2024-03-24 03:10:01       19 阅读
  3. 191. 1个数

    2024-03-24 03:10:01       37 阅读
  4. 191. 1个数

    2024-03-24 03:10:01       32 阅读
  5. 191. 1个数

    2024-03-24 03:10:01       13 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-24 03:10:01       14 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-24 03:10:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-24 03:10:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-24 03:10:01       18 阅读

热门阅读

  1. 架构设计常用到的10种设计模

    2024-03-24 03:10:01       15 阅读
  2. 05- 还在双引号添加字符串?- 文本块

    2024-03-24 03:10:01       18 阅读
  3. vue3中reactive详解

    2024-03-24 03:10:01       20 阅读
  4. 前端视角如何理解“时间复杂度O(n)”

    2024-03-24 03:10:01       17 阅读
  5. 软件测试:C++ Google Test单元测试框架GTest

    2024-03-24 03:10:01       19 阅读
  6. 【Rust】Shared-State Concurrency

    2024-03-24 03:10:01       20 阅读
  7. 计算机二级考试注意事项(Python程序设计篇)

    2024-03-24 03:10:01       16 阅读
  8. perl:获取同花顺数据--业绩预告

    2024-03-24 03:10:01       19 阅读