位运算是C++的一类非常灵活的运算[在其他语言也有支持],数量掌握可以提高程序效率和简介程度。也是很多科技公司入门面试的常见考题。

位运算大致有,

  • 按位异或[XOR]
  • 按位与[AND]
  • 取反[NOT]
  • 按位或[OR]

按位异或(^)

这是一个非常有用的运算符,它的表达是若参加运算的两个二进制位值相同则为0,否则为1,它拥有三个特点。

  1. 0^0=0,0^1=1,0异或任何数=任何数
  2. 1^0=1,1^1=0,1异或任何数=任何数取反
  3. 任何数异或自己=把自己置0

在一道leetcode题中,巧妙使用异或可以快速得到答案。题目如下

Given an array of integers, every element appears twice except for one. Find that single one. Note: Your algorithm should have a linear runtime complexity. Do not use extra memory.

如何设计一个不适用额外内存并且是线性时间复杂度的程序,来找到一个数组中只出现一次的数。题目不难常规解法再次按下不表,介绍一个采用异或的解法。因为A XOR A = 0,且XOR运算是可交换的,于是,对于实例{2,1,4,5,2,4,1}就会有这样的结果:

(2^1^4^5^2^4^1) => ((2^2)^(1^1)^(4^4)^(5)) => (0^0^0^5) => 5

因此,代码也会变得非常简单:

public class Solution {
    public int singleNumber(int[] nums) {
        int len = nums.length;
        int sol = 0;
        for(int i = 0; i < len; ++i) {
            sol ^=nums[i];
        }
        return sol;
    }
}

交换不用临时变量

交换两个值,不用临时变量,也可以通过位异或操作做到。

例如:

a=3,即11(2); b=4,即100(2)

想将a和b的值互换,可以用以下赋值语句实现:

a=a^b; (a=011^100=111 此时a=7)
b=b^a; (b=100^111=011 此时b=3)
a=a^b; (a=111^011=100 此时a=4)

等效于以下两步:

  1. 执行前两个赋值语句,a=a^b;b=b^a;相当于b=b^(a^b)
  2. 再执行第三个赋值语句,a=a^b;。由于a的值等于a^b,b的值等于b^a^b

因此,相当于a=a^b^b^a^b,即a的值等于a^a^b^b^b=b

按位与(&)

按位与是指:参加运算的两个数据,按二进制位进行与运算。如果两个相应的二进制位都为1,则该位的结果值为1;否则为0。这里的1可以理解为逻辑中的true,0可以理解为逻辑中的false。按位与其实与逻辑上与运算(&&)规则一致。逻辑上与运算,要求运算数全真,结果才为真。若,A=true,B=true,则A&B=true。

例如:3&5 3的二进制编码是00000011。[内存储存数据的基本单位是字节(Byte),一个字节由8个位(bit)所组成。位是用以描述电脑数据量的最小单位。二进制系统中,每个0或1就是一个位。将11(2)补足成一个字节,则是00000011]。5的二进制编码是101,将其补足成一个字节,则是00000101。

按位与运算 (下面的写法只是为了方便看):

00000011 &

00000101 =

00000001

由此可知3&5=1

那么按位与有什么用法呢?

清零

若想对一个存储单元清零,即使其全部二进制位为0,只要找一个二进制数,其中各个位符合一下条件:原来的数中为1的位,新数中相应位为0。然后使二者进行&运算,即可达到清零目的。其实对所有数对0进行按位与运算都可以达到这个效果。

取数中某些指定位

比如有一个整数a(2byte),想要取其中的低字节(后8位),只需要将a与8个1按位与即可。 a 00101100 10101100 b 00000000 11111111 c 00000000 10101100

按位或(|)

两个相应的二进制位中只要有一个为1,该位的结果值为1。用法与按位与互为补充。

取反(~)

他是一元运算符,用于求整数的二进制反码,即分别将操作数各二进制位上的1变为0,0变为1。

比如9的二进制是1001,~9就是0110,就是6。


本文部分内容来源互联网