对异或运算符的学习路程
最近在刷洛谷时遇到了 P1161 开灯 这道题,发现了一种非常巧妙的异或解法。本文从常规的数组模拟讲起,再到异或消去,并整理异或运算符的几条核心性质。
方法一:朴素算法 —— 数组模拟
思路
用一个巨大的 bool 数组记录每盏灯的状态,每经过一次就将对应位置取反(开灯 ↔ 关灯),最后遍历整个数组,找到唯一仍亮着的灯。
1 |
|
这种算法简单粗暴,缺点是开了一个巨大的 bool 数组,内存占用较大。
方法二:异或消去
核心原理
异或反转操作的本质:奇数次保留,偶数次抵消。
异或运算符有如下特性:
1 | 1 ^ 1 ^ 2 ^ 2 ^ 3 = 3 |
因此不需要记录每盏灯的状态,只要把所有编号依次异或,出现偶数次的编号会自动抵消,最后剩下的就是答案。
1 |
|
优点
- 省去巨大的 bool 数组,内存占用极小;
- 逻辑优雅,利用数学性质直接消去;
- 时间复杂度与方法一相同,都是
O(Σt)。
深入理解异或运算符 ^
上面的例子只展示了异或的一个巧妙用法,想深入理解可以继续往下看。
异或运算符的性质
- 交换律:
a ^ b = b ^ a - 结合律:
(a ^ b) ^ c = a ^ (b ^ c) - 自反性:
x ^ x = 0(同一个数异或自己直接清零) - 单位元:
x ^ 0 = x(异或 0,值不变)
常见经典应用
- 找出只出现过一次的数字:数组中其他数字都出现两次,把所有数依次异或即可。
- 不借助临时变量交换两个数:
1 | a ^= b; |
异或还有很多有趣的应用,暂时先不深入探究,以后遇到再补充。
