神奇的异或运算

何为异或运算

异或运算是基本bool运算/位运算1的中的一种, C语言里用^表示, 此处用 ⊕ \oplus 表示。
运算规则为,相同为0, 不同为1:
1 ⊕ 1 = 0 0 ⊕ 0 = 0 0 ⊕ 1 = 1 1 ⊕ 0 = 1 1 \oplus 1 = 0 \\ 0 \oplus 0 = 0 \\ 0 \oplus 1 = 1 \\ 1 \oplus 0 = 1 11=000=001=110=1

这是按位运算的规则。
对两个整数进行异或运算时, 实则是对它们的二进制表示按位进行异或运算。例如:
3 = 0011 b 5 = 0101 b 3 ⊕ 5 = 0110 b = 6 3 = 0011b \\ 5 = 0101b \\ 3\oplus 5 = 0110b = 6 3=0011b5=0101b35=0110b=6
式中的 b b b代表为二进制表示。3与5的异或结果为6:6 的二进制表示中的每一位均为3和5二进制表示的对应位的异或结果。

异或运算的特点

异或运算的逆运算是其本身

注意观察这个: 3 ⊕ 5 = 6 3\oplus 5=6 35=6, 6 ⊕ 3 = 5 6\oplus 3=5 63=5, 也就是说, 异或运算是一种可逆2的运算, 而且它的逆运算符就是它自己本身。
对四则运算中的加法来说: a + b = c a + b = c a+b=c a = c − b a = c - b a=cb, 加法的运算效果可以通过减法来消除(逆)掉。
对今天的主角异或运算来说, a ⊕ b = c a \oplus b = c ab=c a = c ⊕ b a = c\oplus b a=cb
更明显的比较:
a ∗ b = c , a = c / b ( b ≠ 0 ) a + b = c , a = c − b a ⊕ b = c , a = c ⊕ b a * b = c, \quad a = c / b \quad (b \neq 0) \\ a + b = c, \quad a = c - b \\ a\oplus b = c, \quad a = c \oplus b ab=c,a=c/b(b̸=0)a+b=c,a=cbab=c,a=cb
示例结束, 就是为了说明异或的逆运算就是其本身的含义。

异或运算与顺序无关

a ⊕ b = b ⊕ a a \oplus b = b \oplus a ab=ba
即,满足交换律和结合律。其实, 与和或运算也是如此。

两种特殊的异或

a ⊕ a = 0 a\oplus a = 0 aa=0
即:自已异或自己得到0。
a ⊕ 0 = a a \oplus 0 = a a0=a
即: 一个数与0异或, 值不变

异或运算的用途

听说异或运算用途很多3。这里举两个常见的例子。

用异或实现swap函数

不用异或实现的swap函数一般长这个样子, 需要定义一个中间变量:

void swap_int(int *a, int *b) {
    int t = *a;
    *a = *b;
    *b = t;
}

使用异或则不需要中间变量4

void swap_int(int *a, int *b) {
    *b = *a ^ *b; 
    *a = *a ^ *b; // *a = *a ^ *a ^ *b --> *b
    *b = *a ^ *b; // *b = *a ^ *a ^ *b ^ *a ^ *b  --> *a
}

初看起来可能有点令人迷糊, 看看注释, 结合上面所讲的运算性质应该就都能明白了。

void swap_int(int *a, int *b) {
	if (a == b) return;
    *b = *a ^ *b; 
    *a = *a ^ *b; // *a = *a ^ *a ^ *b --> *b
    *b = *a ^ *b; // *b = *a ^ *a ^ *b ^ *a ^ *b  --> *a
}

当明白它为什么work时, 你可能会觉得:真妙!
但其实这种实现里有个陷阱。 考虑一下swap_int(&a, &a), 当这样调用后, 无论之前a等于多少, 之后它的值肯定为0。 这明显不合理, 所以, 还需要一点小修改:

找出唯一不重复的数字

想必很多人在面试时都碰到过这道题:

在一堆整数里, 除了一个数字只出现了一次, 其余的数字都出现了两次, 请找出这个只出现一次的数字。

最naive的方法是使用hashmap来完成, 但这样需要的时间与空间复杂度都(相对)太高, 特别是空间上, 如果这堆速度有 1 0 100 10^{100} 10100 个数, 得消耗多少内存啊~
最快最高效的方法就是使用异或运算, 将所有的数字都异或起来, 最终的异或结果就是只出现一次的数字。


  1. 基本bool运算: 与(&)、或(|)、非(~)、异或(^), 括号里为C语言中使用的运算符 ↩︎

  2. 个人理解的可逆,一种非学术性的表达 ↩︎

  3. 没错,就是听说。 个人除了在一些coding面试题里能看到用途, 实际工作中暂时没有碰到过使用合适的场景。 ↩︎

  4. 这是《深入理解计算机系统中》中的一个例子 ↩︎

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值