Java#位运算

最近刷到一道题《二进制中1的个数》,一顿操作猛如虎使用字符串解决了,结果发现自己对位运算的知识掌握还不牢固,位运算牵涉的相关知识也优点模模糊糊,这里赶紧做笔记查漏补缺下,也顺便系统复习下emmm,,,,,,

一、啥叫位运算?

位运算就是把数字用二进制表示之后,对每一位上的0或者1的运算。

二、Java 支持的位运算

  • &:按位与。运算时俩1则1。

  • | :按位或。运算时有1则1

  • ~:按位非。二进制每位取反。

  • ^:按位异或。运算时相同为0,不同为1。

  • <<:左移运算符。左移n位时,最左面去掉n位,最右边补n个0即可。

  • >>:右移运算符。右移n位时,最右面去掉n位,如果数字是一个无符号数值,最左边补n个0即可 如果数字是一个有符号数值,则用数字的符号位填补左边n位。(也就是说原先的数是正数右移n位,左面填补n个0,反之则填补n个1)

  • >>>:无符号右移运算符。运算规则与右移运算符无符号数相同。

ps:这里牵涉到了有符号数、无符号数的概念下面会讲解。

三、位运算法则栗子

在这里插入图片描述

十进制2的按位非计算:首先转换为二进制为0000,0010然后每位取反为1111,1101

十进制10<<2左移运算:首先转换为二进制为0000,1010,左边去两位,右面补俩0结果为00101000

有符号二进制数右移栗子:00001010>>2 ,因为这个数最高位为符号位0则这个数是正数,则右面去掉两位,左面补俩0结果为00000010。10001010>>3,因为这个数最高位为符号位1则这个数是负数,则右面去掉三位,左面补三1结果为11110001

四、计算机中的正负数表示

首先介绍下计算机中符号类型的规定:在计算机中,可以区分正负的类型,称为有符类型(signed),无正负的类型(只有正值),称为无符类型。 (unsigned)。具体采用有符号类型来计算数据还是采用无符号类型来计算数据这个不必疑惑,这个是事先规定好的。

1、无符号数、有符号数表示范围区别

拿一字节8位二进制数来表示整数的话有两种表示法:
1、无符号数,这时不用区分负数。表示数大于等于0即可。所以8位可表示2^8-1 = 255个数。为啥要减一呢因为0000 0000 表示0占据了一个数,1111 1111最大255。所以8位无符号数表示范围为【0,255】
2、有符号数,这时需要区分符号位,计算机中使用最高位区分符号位。这时还剩下7位。这时这7位只能表示范围【0,127】,由于是有符号的最高位为0就是【0,127】,最高位为1就是【-1,-128】,所以两区间并集为【127,-128】
当然我们也可以使用32位,64位来表示数了,这样表示的范围更大。

2、计算机中正负数的表示

计算机规定:
1、采取补码表示数。
2、正数的原码,补码,反码规则:

a、原码:按照绝对值大小转换成的二进制数。即是原码。
b、反码:正数的反码是其本身也即与原码相同。
c、补码:正数的补码是其本身也即与原码相同。

3、负数的原码,补码,反码计算规则如下:

a、原码:一个整数,按照绝对值大小转换成的二进制数,最高位表示符号位(0表示正,1表示负),称为原码。例如-1原码用8位二进制表示1000 0001
b、反码:将原码除去符号位的位数按位取反,得到反码。例如-1的源码1000 0001,则其反码为1111 1110
c、补码:反码+1.如-1补码1111 1111。这也就是计算机中的-1八位二进制表示。

4、0的反码补码都是0

ps:是不是顿时明白了为啥自己总以为1000 0001表示-1而实际-1用1111 1111表示emmmm,,

五、Java 提供的进制转化

1、Integer类

1、Integer.toBinaryString(int n):十进制转二进制。负数输出的为其32位(int 4字节32位)的反码
2、Integer.toHexString(int n):十进制转十六进制
3、Integer.toOctalString(int n): 十进制转成八进制
4、更多转换参考api valueOf,parseInt。。。。

六、例题

输入一个整数,输出该数的二进制中含有1的个数

解法1:与位运算无关。

    private static int binaryOneCount(int n) {

        String binaryNumber = Integer.toBinaryString(n); //输出的为反码
        int count = 0;
        for (int i = 0; i < binaryNumber.length(); i++) {
            char c = binaryNumber.charAt(i);
            if ('1' == c) {
                count++;
            }
        }
        System.out.println("转为二进制:" + binaryNumber + " 含有1的个数:" + count);
        return count;
    }
解法2:使用右移>>和与&位运算解

    private static int method2(int n) {
        int count = 0;
        while (n != 0) {//有符号数,负数时会陷入死循环。
            if ((n & 1) == 1) count++; //& 运算有清零的作用
            n = n >> 1;
        }
     
        return count;
    }

原理:
1、如1111 0010>> 1 ,不断右移则右侧不断去掉1位。1111 0010最终会变为0
2、&1具有清零的功效
bug:
有符号负数会陷入死循环
bug 分析:最终n= -1 时其补码为1111 1111 无论如何右移都是-1造成死循环。

解法3:规律+&运算

规律:吧一个整数减去1与原来的数做&运算。会把这个整数中二进制最右面的1变为0
总结:复杂度较低,二进制中有几个1 或循环几次。

  private static int method3(int n) {
        int count = 0;
        while (n != 0) {
            count++; 
            n = (n-1)&n;
        }
        return count;
    }

end

参考:
1、剑指offer-位运算
2、https://www.cnblogs.com/lazycoding/archive/2011/03/21/unsigned-signed.html
3、https://blog.csdn.net/szwangdf/article/details/2601941

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值