摘要:本文介绍了异或运算的特点和运算规律,并通过利用异或运算解决了一个算法题目:找出1-n连续数组中的惟一重复数字。
异或运算的巧妙应用:快速找出重复数字
引言
异或运算(XOR)是一种基础的二进制运算,在编程面试和算法设计中有着意想不到的妙用。本文将深入探讨如何利用异或运算的数学特性,以 O(n) 时间复杂度和 O(1) 空间复杂度找出数组中的重复数字。
问题定义
给定一个长度为 n+1 的数组,包含 1 到 n 之间的整数,恰好有一个数字重复出现一次,其余数字各出现一次。例如:
- 输入:
[1, 2, 3, 4, 5, 5] - 输出:
5
异或运算的基本性质
在深入解决方案之前,先回顾异或运算的关键特性:
- 交换律 :
a ⊕ b = b ⊕ a - 结合律 :
(a ⊕ b) ⊕ c = a ⊕ (b ⊕ c) - 自反性 :
a ⊕ a = 0 - 单位元 :
a ⊕ 0 = a - 消去律 :
a ⊕ b ⊕ a = b
背后是位运算的本质。
0 ⊕ 0 = 0, 0 ⊕ 1 = 1
通俗理解就是: 0和0不相等吗?否,他们相等,否用0表示, 是用1表示,所以前面的0是数字,后面的0是否, 位运算的0和1的双重含义是位运算的基础。
这些性质意味着异或运算可以任意重新组合和抵消相同项。
常见误区与正确解法
误区:直接对数组所有元素进行异或
python
xor_all = 1 ⊕ 2 ⊕ 3 ⊕ 4 ⊕ 5 ⊕ 5
= (1 ⊕ 2 ⊕ 3 ⊕ 4) ⊕ (5 ⊕ 5)
= (1 ⊕ 2 ⊕ 3 ⊕ 4) ⊕ 0
= 4
结果不是重复数字,而是其他数字的异或值。直接异或整个数组无法找出重复数,因为重复数出现两次会相互抵消。
正确解法:数组异或与范围异或相结合
设数组为 A,长度为 n+1,包含 1 到 n 的整数,其中一个数字重复。
计算:
- 数组所有元素的异或值:
xor_arr = A[0] ⊕ A[1] ⊕ ... ⊕ A[n] - 1 到 n 的异或值:
xor_range = 1 ⊕ 2 ⊕ ... ⊕ n - 重复数字 =
xor_arr ⊕ xor_range
原理分析
将两个异或值合并考虑:
ini
xor_arr ⊕ xor_range
= (每个元素按出现次数异或) ⊕ (1到n各出现一次)
- 非重复数字:在数组中出现 1 次,在 1~n 中出现 1 次 → 总共出现 2 次 → 异或抵消
- 重复数字:在数组中出现 2 次,在 1~n 中出现 1 次 → 总共出现 3 次 → 保留该数字
因为:
css
a ⊕ a ⊕ a = (a ⊕ a) ⊕ a = 0 ⊕ a = a
实例验证
对于数组 [1, 2, 3, 4, 5, 5](n=5):
xor_arr = 1 ⊕ 2 ⊕ 3 ⊕ 4 ⊕ 5 ⊕ 5 = 1 ⊕ 2 ⊕ 3 ⊕ 4 = 4xor_range = 1 ⊕ 2 ⊕ 3 ⊕ 4 ⊕ 5- 逐步计算:
1 ⊕ 2 = 3,3 ⊕ 3 = 0,0 ⊕ 4 = 4,4 ⊕ 5 = 1
- 逐步计算:
- 重复数字 =
4 ⊕ 1 = 5
✅ 正确找出重复数字 5。
算法实现
Python 实现
python
def find_duplicate(nums):
n = len(nums) - 1
# 计算数组所有元素的异或
xor_arr = 0
for num in nums:
xor_arr ^= num
# 计算 1 到 n 的异或
xor_range = 0
for i in range(1, n + 1):
xor_range ^= i
# 重复数字
return xor_arr ^ xor_range
# 更简洁的写法
def find_duplicate_compact(nums):
result = 0
for i, num in enumerate(nums):
result ^= num ^ (i + 1)
return result ^ len(nums) # 修正边界
时间复杂度与空间复杂度
- 时间复杂度:O(n),只需遍历数组一次
- 空间复杂度:O(1),只使用常数个额外变量
扩展应用
变体问题:找到重复和缺失的数字
如果数组长度为 n,包含 1 到 n 的数字,但有一个数字重复,一个数字缺失,可以使用类似的异或技巧:
- 计算
xor_all = (所有数组元素异或) ⊕ (1到n异或) xor_all的二进制表示中最低位的 1,表示重复数和缺失数在该位不同- 根据该位将数字分成两组,分别异或得到两个数字
- 判断哪个是重复的,哪个是缺失的
其他应用场景
- 找出现奇数次的数字:在一组数字中,只有一个数字出现奇数次,其余出现偶数次
- 交换两个变量:不使用临时变量交换两个整数
- 加密中的简单混淆:基于异或的简单加密算法
注意事项
- 前提条件:该方法要求恰好一个数字重复,且数字范围明确(1 到 n)
- 边界情况:注意数组索引和范围的对应关系
- 溢出问题:异或运算不会引起溢出问题
总结
异或运算不仅是一种基本的逻辑运算,更是算法设计中的有力工具。通过巧妙的数学变换,我们可以:
- 利用自反性消除成对出现的数字
- 结合范围异或来分离重复项
- 在 O(1) 额外空间内解决问题
这种解法体现了计算机科学中一个重要的思想:通过数学变换将问题转化为更容易处理的形式。掌握异或运算的特性,对于解决位操作相关问题有着重要意义。
思考题
- 如果数组中有两个数字重复,还能用异或法解决吗?
- 如果数字范围不是从 1 开始,而是从 m 到 n,方法需要如何调整?
- 如何用异或运算判断一个整数是否是 2 的幂?
关键点:异或运算的抵消特性使其成为处理"成对出现"问题的理想工具,而结合数学范围限制可以进一步分离出异常项。