题目难度: 简单
今天继续更新剑指 offer 系列, 这道题很经典, 大家可能已经熟悉两种方法了, 这里额外提供两种方法 🤩 供大家参考~
老样子晚上 6 点 45 分准时更新公众号 每日精选算法题, 大家记得关注哦~ 另外在公众号里回复 offer 就能看到剑指 offer 系列当前连载的所有文章了
题目描述
请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。
题目样例
示例 1
输入
00000000000000000000000000001011
输出
3
解释
输入的二进制串 00000000000000000000000000001011 中,共有三位为 ‘1’。
示例 2
输入
00000000000000000000000010000000
输出
1
解释
输入的二进制串 00000000000000000000000010000000 中,共有一位为 ‘1’。
题目思考
- 可以使用哪些位运算?
- 不用位运算可以吗?
解决方案
方案 1 & 2 - 直接统计 1 的个数
分析
- 最直观的思路就是直接统计二进制数字里 1 的个数
实现
- 方案 1: 转成二进制字符串统计’1’的个数
- 方案 2: 移位, 根据最低位是否为 1 来统计. 注意题目是 32 位整数, 所以限制移位次数为 32 次, 避免负数的高位一直为 1 即可
复杂度
- 时间复杂度
O(logN)
- 数字 N 的位数为
logN
, 需要遍历这么多次
- 数字 N 的位数为
- 空间复杂度
O(1)
- 只使用了几个变量
代码
方案 1: 转二进制字符串统计 1
class Solution:
def hammingWeight(self, n: int) -> int:
# 方法1: 转二进制字符串统计1
return bin(n).count('1')
方案 2: 循环移位统计 1
class Solution:
def hammingWeight(self, n: int) -> int:
# 方法2: 循环移位统计1
res = 0
for i in range(32):
if n == 0:
break
res += n & 1
n >>= 1
return res
方案 3 & 4 - 利用位运算统计 1 的个数
分析
- 如果我们能直接定位到 1, 然后每次都把已经统计的 1 排除掉的话, 就只需要操作 1 的个数次, 而不需要遍历所有位了
实现
- 方案 3: 利用
n & (n-1)
. 大家可以想象一下 n-1 在二进制上所做的操作: 其实就是把 n 的最低一位的 1 转成 0, 然后后面的 0 都转成 1, 高位保持不变, 这样它们的&
值自然就会去掉最低位的 1. 这样循环操作, 最终就会去掉所有 1, 得到 0, 操作的次数也即 1 的个数了. - 方案 4: 利用
n & -n
. 熟悉树状数组的同学可能会比较清楚这个表达式, 这个就是 lowbit. 通俗点说它相比 n, 只保留最低位的 1, 其他位全清 0. 这个同样可以根据-n
的二进制含义得出, 补码与原数的&
的结果会正好只留最低位的 1. 然后将 n 减去这个 lowbit, 就等于去掉了一个 1, 循环最终会得到 0, 操作的次数也即 1 的个数了.
复杂度
- 时间复杂度
O(M)
- 设数字有
M
位 1, 需要遍历这么多次
- 设数字有
- 空间复杂度
O(1)
- 只使用了几个变量
代码
方案 3: n &= n - 1
class Solution:
def hammingWeight(self, n: int) -> int:
# 方法3: n &= n - 1
res = 0
while n:
n &= n - 1
res += 1
return res
方案 4: n -= n & -n
class Solution:
def hammingWeight(self, n: int) -> int:
# 方法4: n -= n & -n
res = 0
while n:
n -= n & -n
res += 1
return res
大家可以在下面这些地方找到我~😊
我的公众号: 每日精选算法题, 欢迎大家扫码关注~😊