知识点:位运算和二进制。
题目描述
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
1:什么是位运算
位运算是把数字用二进制表示之后,对每一位上0或1的运算。二进制及其位运算是现代计算机科学的基石,很多底层的的技术都离不开位运算 。
与:&(有0为0);
或:|(有1为1) ;
异或:^(不同为1) ;
**左移运算符:**m<<n(把m左移n位,最左边的n位将被丢弃,右边补上n个0);
**右移运算符:**m>>n(把m右移n位,最右边n位将被丢弃:如果数字是一个无符号整数,则用0填补最左边的n位;如果 数字是一个有符号的数值,则用数字的符号位填补最左边的第n位。也就是说数字原先是一个正数,则右移之后在左边补n个0,如果数字原先是负数,则右移动之后在左边补n个1.例如:00001010>>2=00000010 /10001010>>>3 11110001)
2:Java中的整数类型
在Java中,整数数据类型可以分为long,int,short以及byte类型;
long(长整型)为64位,也就是8个字节(bytes)可以表示的范围是:-2 ^63 (-9223372036854775808)~2 ^63-1(9223372036854775807);
int为32位,也就是4个字节(bytes),可以表示的范围是-2 ^31(-2147483648)~ 2 ^31-1(2147483647);
short(短整型)为16位,也就是两个字节(bytes),可以表示的范围是(-32768~32767);
byte类型(字节类型),也就是一个字节(bytes),可以表示的范围是(-128~127);
3:计算机中的整数的保存
计算机中带符号的整数采用二进制的补码进行存储
正数:原码,反码,补码都是一致的;
负数:(观察补码的转换,题目中说负数是以补码的形式存在)
/一个整数按照绝对值大小转换成的二进制数,是为原码。
5的原码:00000101。/
反码是和原码反着来的。
反码加一叫补码。
补码就是负数在计算机中的二进制表示方法。那么,11111011表示8位的-5;
如果要表示16位的-5 ,在左边添上8个1即可。
4:分析题目《剑指Offer》
基本思路:先判断整数二进制表示中最右边一位是不是1;接着把输入的整数右移一位,再判断是不是1;这样直到整个整数变为0为止。(判断整数的最右边是不是1需要把整数和1做位于运算看结果是不是0)。代码如下(运行没有通过,提示循环有错或者算法复杂度过大)
public class Solution{
public int NumberOf1(int n){
int count=0;
while(n!=0){
if((n&1)==1)
{count++;}
n=n>>1;
}
return count;
}
}
上面的代码有一个问题:如果那个数字是一个负数。如0x80000000右移一位,不是变成了0x40000000,而是变成了0xC0000000(因为高位补1了)。所以移位后的最高位会被设置为1.那么最终这个数字会变成0xFFFFFFF而陷入死循环。
把整数右移一位和把整数除以2在数学上是等价的,但是除法的效率比位运算低得多,在实际的编程中应尽可能的使用移位运算代替除法
3:下面是通过的代码
针对上述的问题,我们可以补右移数字n。首先把n和1做位于运算,判断n的最低是不是1.接着把1左移一位得到2,再和n做与运算,,,,,反复左移;在这个解法中循环的次数是32次(相当于整数二进制的位数)
代码如下面
public class Solution{
public int NumberOf1(int n){
int count=0;
int flag=1;
while(flag!=0){
if((n&flag)==flag){count++;}
flag=flag<<1;
}
return count;
}
}
下面是一个惊喜的做法:
1:先分析一个问题(一个整数减去1)
如果一个整数不等于0,那么该整数的二进制表示至少有一位是1:如果这个数的最右边是1,那么减去1之后,最后一位变成0而其他所有位都保持不变,也就是最后一位相当于做了取反操作,由1变成了0;如果最后一位不是1是0,假设该整数二进制表示中最右边的1位于第m位,那么减去1的时候,第m位由1变成了0,第m位之后的数字都变成了1,第m位前面的数字都不变。
从上面的案例启发:把一个整数减去1,再和原整数进行与运算,会把该整数最右边的1变成0;那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作。基于这种思路,写出如下代码:
public class Solution{
public int NumberOf1(int n){
int count=0;
while(n!=0){
count++;
n=n&(n-1);
}
return count;
}
}
观察下图:-5的补码(以byte类型存储)有
最终调试代码如下:
package 一个整数二进制数中1的位数;
public class Solution{
public static int NumberOf1(int n){
int count=0;
while(n!=0){
count++;
n=n&(n-1);
}
return count;
}
}
下面是测试代码:
package 一个整数二进制数中1的位数;
public class Test {
public static void main(String[] args) {
int flag1 = Solution.NumberOf1(5);
System.out.println(flag1);
int flag2 = Solution.NumberOf1(0);
System.out.println(flag2);
int flag3 = Solution.NumberOf1(-5);
System.out.println(flag3);
}
}
下面是运行结果,计算机默认的是32位,也就是long类型的数据。(彻彻底底的搞懂了啊!!!!!!!!不将就!!!!!)