题目描述
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
知识点:
(1)按位与(&)
两位全为1,结果才为1. 例:0&1=0 1&1=1
作用:1.清零 2.取一个数中指定数: 例:X=10101110 取X得低四位。 X&00001111=0000 1110
(2)按位或( |)
只要有一个位1,结果就位1.
作用:常用来对一个数据的某些位 置1. 例:X=00001001 使X低四位为1 X|0000 1111=00001111
(3)异或(^)
两个相应位值不同,则该位结果为1,否则为0.
作用:1.使特定位翻转,找一个数,对应X要翻转的各位,该数的对应位为1,其余位为0,此数与X对应位 异或即可。
例:X=10101110 使X低四位翻转
X^0000 1111=10100001
2.与0相异或,保留自身原值。(应用在两个变量交换值,效率最高)
例:A=A^B,B=A^B,A=A^B 这样A和B的值就交换了。
(4)取反(~)
对一个二进制数按位取反。~0=1
例:在java中对有符号int型数取反运算。 注:在java中有符号整数都是用补码表示的。
补码的知识:
原码:一个整数按照绝对值大小转换成的二进制数称为原码。
反码:1.正数的反码与其原码相同;2.负数的反码是对其原码逐位取反,但符号位除外。原码10010= 反码11101 (10010,1为符号码,故为负)(11101) 二进制= -2 十进制
补码:补码表示法规定:正数的补码与其原码相同;负数的补码是在其反码的末位加1。
取反方法:1.对正数:先对其表示的二进制取反,得到负数的补码;然后求出反码,最后求出原码。(即为所求数)
例:~3
java中int类型的长度是四个字节,也就是32位,so:
00000000 00000000 00000000 00000011
取反: 1... 11111100 最高位为1,所以这是负数的补码形式。
求出反码: 1... 11111011
求出原码: 0... 00000100 数字为4
之前此数的补码最高位为1,所以 ~3=-4
2.对负数:先求负数的补码,然后直接取反。(注意最高位1代表负数,0代表整数)。
(5)左移(<<)
将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)
例如: 2<<1 =4 10<<1=100
若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2。
例如:
11(1011)<<2= 0010 1100=22
11(00000000 00000000 00000000 1011)整形32bit
(6)右移(>>)
将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。若右移时舍高位不是1(即不是负数),操作数每右移一位,相当于该数除以2。
左补0还是补1得看被移数是正还是负。
例如:4>>2=4/2/2=1
-14(即1111 0010)>>2 =1111 1100=-4
解题思路:
参考博客:https://www.cnblogs.com/edisonchou/p/4752086.html
1.对操组数右移:整数的最低位和1做与运算,如果结果为1,则这一位是1。然后将这个数右移,继续用该数的第二位和1做与运算判断。但是会存在一个问题。如果是负数的话,一直右移最后这个数会变为全1,然后和1做与一直为True,导致陷入死循环。所以这种方法不可取。
2.那么和1相对应的方法就是操作数不改变,将1不断的左移,和二进制的每一位分别做与运算。这种操作是可取的,不过效率较低,因为二进制的int占32位,所以flag(1)就要分别和这个二进制的32位中的每一位做与运算,才能得到最终的判断,所以需要循环32次。
3.把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0。那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作。
代码实现
实现1(低效的)
public class Solution {
public int NumberOf1(int n) {
int flag=1;
int count=0;
while(flag!=0){
if((n&flag)!=0){
count+=1;
}
flag=flag<<1;
}
return count;
}
}
实现2:
public class Solution {
public int NumberOf1(int n) {
int count=0;
while(n!=0){
count++;
n=n&(n-1);
}
return count;
}
}