剑指Offer-其他-(1)

知识点:位运算和二进制。

题目描述
输入一个整数,输出该数二进制表示中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类型的数据。(彻彻底底的搞懂了啊!!!!!!!!不将就!!!!!)
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_35649064/article/details/84769305