1. 题目来源
链接:二进制中1的个数
来源:LeetCode——《剑指-Offer》专项
2. 题目说明
请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。
示例1 :
输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 ‘1’。
示例 2:
输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 ‘1’。
示例 3:
输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 ‘1’。
3. 题目解析
方法一:常规左移、右移位运算解法
题意很明确的一道位运算问题,首先普及两个知识点:
- 正数右移运算,会在最高位补 0,而负数右移运算,会给最高位补 1,也就是补符号位。常见的解法如果将数字
n
,循环右移并& 1
,当n
变化为 0 时,循环结束。很明显,这个只有当n
为正数才适用,负数会构成死循环 - 左移运算就在末尾补零即可,这个不区别正负数
解决负数死循环问题,可以将 1 循环左移并 &n
即可。
下面是些别名:
typedef unsigned char uchar;
typedef uchar uint8;
typedef uint8 TFlowPortId;
typedef unsigned short ushort;
typedef ushort uint16;
typedef uint16 TFlowNodeId;
typedef unsigned int uint;
typedef uint uint32;
参见代码如下:
// 执行用时 :0 ms, 在所有 C++ 提交中击败了100.00%的用户
// 内存消耗 :8.1 MB, 在所有 C++ 提交中击败了100.00%的用户
class Solution {
public:
int hammingWeight(uint32_t n) {
int count = 0;
uint32_t flag = 1;
while (flag) {
if (n & flag)
count++;
flag = flag << 1;
}
return count;
}
};
在这个人觉得采用 while(flag)
进行判断不够明确,采用 for
进行 32 次即可。
方法二:位运算技巧
n = n & (n -1)
相当于把整数的二进制表示中最右边的 1 变成 0。很多二进制都可以用这个思路解决。
参见代码如下:
// 执行用时 :4 ms, 在所有 C++ 提交中击败了73.72%的用户
// 内存消耗 :8.2 MB, 在所有 C++ 提交中击败了100.00%的用户
class Solution {
public:
int hammingWeight(uint32_t n) {
int count = 0;
while (n) {
n = n & (n - 1);
++count;
}
return count;
}
};
方法三:位运算常见问题总结
位运算问题真的考智商,这个需要多多积累吧,尤其第二种方法很常见的。在此再给列出《剑指-Offer》中常见的位运算问题,做以复习:
- 用一条语句判断一个整数是不是 2 的整数次方。一个整数如果为 2 的整数次方,那么它的二进制表示中有且仅有 1 位是 1,而其它所有位均为 0。采用方法二
n = n & (n -1)
,一步搞定! - 输入两个整数 m 和 n,计算需要改变 m 的二进制表示中的多少位才能得到 n。这个问题也很常见,好像
LeetCode
周赛就有一个相当类似的问题。第一步:求两个数的异或。第二步:统计异或结果中 1 的位数。 搞定!