面试题10:请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。例如把9表示成二进制是1001,有2位是1。所以若是输入9,该函数输出2。面试
牛客网OJ:二进制中1的个数bash
位运算无外乎 与、或、异或、左移和右移 5 种类型的运算。函数
使用位运算符进行运算时,整数会自动转为二进制形式,再进行位运算。全部位运算的题型基本都是各类类型位运算的组合。ui
注意点:整数包含正、负数。spa
负数右移须要在真空位补上1,如-1,二进制原码为 1000 0001,第一个位为符号位,负数为1,非负数为0开头。在计算机中,负数采用补码来表示,-1的最终二进制表现形式为: 绝对值取反加1,附带上符号位。即 111 1111 加上符号位为 1111 1111。3d
-1 >> 1 的结果是 1111 1111。code
判断二进制数中1的位数,能够经过和整数 1 进行 与 运算,1&1 = 1,1&0 = 0。
1 的二进制形式为:0000 0001
9 的二进制形式为:0000 1001
9&1 = 0000 0001 != 0,能够肯定最后一位是 1。
那么此时要如何继续往前判断另外一个1呢?
解决方案:
将 1 左移一位,即 0000 0001 变为 0000 0010,再判断 9(0000 1001)的第 2 位
0000 0010 & 0000 1001 = 0000 0000 == 0,能够肯定第 2 位为 0。
这个过程当中,使用一个中间计数器变量 count 每次肯定与运算结果为非 0, 则加 1 便可统计 1 的个数。
再将 1 左移一位,即 0000 0010 变为 0000 0100,再判断 9(0000 1001)的第 3 位
0000 0100 & 0000 1001 = 0000 0100 != 0,能够肯定第 3 位为 1。
后面同上,1 挨个左移,直到移动到最左边,变成 0000 0000,宣告位遍历结束:
0000 0001
0000 0010
0000 0100
0000 1000
0001 0000
0010 0000
0100 0000
1000 0000
0000 0000 结束
复制代码
public class Solution {
public int NumberOf1(int n) {
int count = 0;
int flag = 1;
while(flag != 0){
if((n&flag) != 0){
count++;
}
flag = flag << 1;
}
return count;
}
}
复制代码
位运算的题首先要想到位移运算和 与或 运算的结合。cdn
一个整数若是是2的整数次方,那么它的二进制表示中有且只有一位是1,而其余全部位都是0。blog
方案:把这个整数减去1以后再和它本身作与运算,这个整数中惟一的1就会变成0。
好比 8 的二进制位 0000 1000,0000 1000 - 1 = 0000 1000 - 0000 0001 = 0000 0111
而后再将计算结果和 8 自身进行与运算:0000 1000 & 0000 0111 = 0000 0000 结果恰好是 0 。
复制代码
if((n - 1) & n == 0){
//是2的整数次方
}
复制代码
好比 10 的二进制表示为 1010,13 的二进制表示为 1101,须要改变 1010 中的 3 位才能获得 1101。get
咱们能够分为两步解决这个问题:第一步求这两个数的异或,第二步统计异或结果中1的位数。
1010 ^ 1101 = 0111
再运用移位运算 + 与运算判断 1 的个数。
复制代码
public int numOfBitToChange(int m, int n) {
int result = m ^ n;
int count = 0;
int flag = 1;
while(flag != 0) {
if((result & flag) != 0) {
count ++;
}
flag << 1;
}
return count;
}
复制代码
把一个整数减去1以后再和原来的整数作位与运算,获得的结果至关因而把整数的二进制表示中的最右边一个1变成0。
5 - 1 = 0101 - 0001 = 0100
0101 & 0100 = 0100 (最右边的 1 变成0:0101 --> 0100)
4 - 1 = 0100 - 0001 = 0011
0100 & 0011 = 0000 (最右边的 1 变成0:0100 --> 0000)
10 - 1 = 1010 - 0001 = 1001
1010 & 1001 = 1000 (最右边的 1 变成0:1010 --> 1000)
复制代码
不少二进制的问题均可以用这个思路解决。