算法通关村--位运算常用技巧

位运算是计算机的核心基础,数据的表示和计算几乎都少不了。所以我们深入学习下位运算。

数字在计算机中的表示

机器数:一个数在计算机中的二进制表示形式,叫做这个数的机器数。并且这个数是带符号的,0表示正数,1表示负数。

真值:机器数的第一位表示符号位,剩余位的值转化为十进制,就是真值。

原码:第一位为符号位,剩余位数表示值。

反码:正数的反码与原码一致;负数的反码就是符号位不变,其余各位取反。

补码:正数的补码与原码一致;负数的补码就是符号位不变,其余各位取反,最后加1。

位运算规则

位运算主要有:与、或、异或、取反、左移、右移,其中左移和右移统称为移位运算,移位运算又分为算术移位和逻辑移位。

与:符号& 两个数对应的位都为1时,结果为1,否则为0;

或:符号 | ,两个数对应的位都为0时,结果为0,否则为1;

异或:符号⊕(在代码中用^表示),两个数对应的位不相等时,结果为1,否则为0;

取反:符号~,对一个数的每个二进制位进行取反操作

移位运算

移位运算按照移位方向分为左移和右移,按照是否带符号分类可以分为算术移位和逻辑移位。

左移运算:符号 <<,左移运算时,将全部二进制向左移动若干位,高位丢弃,低位补0(除负数反码:补1);

右移运算:符号 >>, 右移运算时,将全部二进制向右移动若干位,低位丢弃,高位的补位由算术移位或逻辑移位决定:

        算术右移:

                正数:

                        原码、补码、反码:补0

                负数:

                        原码:补0;

                        补码:补1;

                        反码:补1;

        逻辑右移:高位补0;

在Java中,所有的表示整数的类型都是有符号类型,所以要区分算术右移和逻辑右移。在Java中,算术右移的符号是 >>,逻辑右移的符号是 >>>。

移位运算和乘除法的关系

左移运算对应乘法运算。将一个数左移k位,等价于将这个数乘以2^k。当乘数不是2的整次幂时,可以将乘数拆成若干项2的整数次幂的和。对于任意整数,乘法运算都可以用左移运算实现,但是需要注意溢出情况。

算术右移运算对应除法运算,将一个数右移k位,相当于将这个数除以2^k。

位运算常用技巧

假设以下出现的变量都是有符号整数。

幂等律:a & a = a,a | a = a(异或不满足幂等律);

交换律:a & b = b & a,a | b = b | a,a ⊕ b = b ⊕ a;

结合律:(a & b)& c = a &(b & c),(a | b)| c = a |(b | c),(a ⊕ b)⊕ c = a ⊕(b ⊕ c);

分配律:(a & b)| c =(a | c)&(b | c),(a | b)& c =(a & c)|(b & c),(a ⊕ b)& c = (a & c)⊕(b & c);

德摩根律:~(a & b)= (~a)|(~b),~(a | b)= (~a)&(~b);

取反运算性质:-1 =~0,-a = ~(a - 1);

与运算性质:a & 0 = 0,a & (-1)= a,a & (~a)= 0;

或运算性质:a | 0 = a;

异或运算性质:a ⊕ 0 = a, a ⊕ a = 0;

获取

该方法是将1左移 i 位,得到形如00010000的值,接着堆这个值与num执行“位与”操作,从而将 i 位之外的所有位清零,最后检查该结果是否为零。不为零说明 i 位为1,否则 i 位为0。

boolean getBit (int num,int i){
        return ((num & (1 << i)) != 0);
    }

设置(将某个值设置为1)

setBit先将1左移 i 位,得到形如00010000的值,接着堆这个值和num执行“位或”操作,这样只会改变 i 位的数据。这样除 i 位外位的位均为零,故不会影响num的其余位。

int setBit (int num,int i){
        return num | (1 << i);
    }

清零(将某一位置设置为0)

该方法与setBit相反,首先将1左移 i 位获得形如00010000的值,对这个值取反进而得到类似11101111的值,接着对该值和num执行“位与”,故不会影响到num的其余位,只会清零 i 位。

int clearBit(int num,int i){
        int mark = ~(1 << i);
        return num & mark;
    }

更新

这个方法是将setBit和clearBit合二为一,首先用诸如11101111的值将num的第 i 为清零。接着将带写入值v左移 i 位,得到一个 i 位为v但其余位都为0的数,最后对之前的结果执行“位或”操作,v为1这num的 i 位更新为1,否则为0;

int updateBit(int num,int i,int v){
        int mark = ~(1 << i);
        return (num & mark) | (v << i);
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值