位运算第1篇-异或运算-快速找出重复数字

摘要:本文介绍了异或运算的特点和运算规律,并通过利用异或运算解决了一个算法题目:找出1-n连续数组中的惟一重复数字。

异或运算的巧妙应用:快速找出重复数字

引言

异或运算(XOR)是一种基础的二进制运算,在编程面试和算法设计中有着意想不到的妙用。本文将深入探讨如何利用异或运算的数学特性,以 O(n) 时间复杂度和 O(1) 空间复杂度找出数组中的重复数字。

问题定义

给定一个长度为 n+1 的数组,包含 1 到 n 之间的整数,恰好有一个数字重复出现一次,其余数字各出现一次。例如:

  • 输入:[1, 2, 3, 4, 5, 5]
  • 输出:5

异或运算的基本性质

在深入解决方案之前,先回顾异或运算的关键特性:

  1. 交换律a ⊕ b = b ⊕ a
  2. 结合律(a ⊕ b) ⊕ c = a ⊕ (b ⊕ c)
  3. 自反性a ⊕ a = 0
  4. 单位元a ⊕ 0 = a
  5. 消去律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 的整数,其中一个数字重复。

计算:

  1. 数组所有元素的异或值:xor_arr = A[0] ⊕ A[1] ⊕ ... ⊕ A[n]
  2. 1 到 n 的异或值:xor_range = 1 ⊕ 2 ⊕ ... ⊕ n
  3. 重复数字 = 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):

  1. xor_arr = 1 ⊕ 2 ⊕ 3 ⊕ 4 ⊕ 5 ⊕ 5 = 1 ⊕ 2 ⊕ 3 ⊕ 4 = 4
  2. xor_range = 1 ⊕ 2 ⊕ 3 ⊕ 4 ⊕ 5
    • 逐步计算:1 ⊕ 2 = 33 ⊕ 3 = 00 ⊕ 4 = 44 ⊕ 5 = 1
  3. 重复数字 = 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 的数字,但有一个数字重复,一个数字缺失,可以使用类似的异或技巧:

  1. 计算 xor_all = (所有数组元素异或) ⊕ (1到n异或)
  2. xor_all 的二进制表示中最低位的 1,表示重复数和缺失数在该位不同
  3. 根据该位将数字分成两组,分别异或得到两个数字
  4. 判断哪个是重复的,哪个是缺失的

其他应用场景

  1. 找出现奇数次的数字:在一组数字中,只有一个数字出现奇数次,其余出现偶数次
  2. 交换两个变量:不使用临时变量交换两个整数
  3. 加密中的简单混淆:基于异或的简单加密算法

注意事项

  1. 前提条件:该方法要求恰好一个数字重复,且数字范围明确(1 到 n)
  2. 边界情况:注意数组索引和范围的对应关系
  3. 溢出问题:异或运算不会引起溢出问题

总结

异或运算不仅是一种基本的逻辑运算,更是算法设计中的有力工具。通过巧妙的数学变换,我们可以:

  1. 利用自反性消除成对出现的数字
  2. 结合范围异或来分离重复项
  3. 在 O(1) 额外空间内解决问题

这种解法体现了计算机科学中一个重要的思想:通过数学变换将问题转化为更容易处理的形式。掌握异或运算的特性,对于解决位操作相关问题有着重要意义。

思考题

  1. 如果数组中有两个数字重复,还能用异或法解决吗?
  2. 如果数字范围不是从 1 开始,而是从 m 到 n,方法需要如何调整?
  3. 如何用异或运算判断一个整数是否是 2 的幂?

关键点:异或运算的抵消特性使其成为处理"成对出现"问题的理想工具,而结合数学范围限制可以进一步分离出异常项。

相关推荐
xxxxxmy2 小时前
同向双指针(滑动窗口)
python·算法·滑动窗口·同向双指针
释怀°Believe2 小时前
Daily算法刷题【面试经典150题-5️⃣图】
算法·面试·深度优先
List<String> error_P2 小时前
数据结构:链表-单向链表篇
算法·链表
ss2732 小时前
ConcurrentHashMap:扩容机制与size()方法
算法·哈希算法
2401_860319522 小时前
在React Native鸿蒙跨平台开发中实现一个冒泡排序算法并将其应用于数据排序,如何进行复制数组以避免直接修改状态中的数组
javascript·算法·react native·react.js·harmonyos
im_AMBER2 小时前
Leetcode 72 数组列表中的最大距离
c++·笔记·学习·算法·leetcode
编程饭碗2 小时前
【Java循环】
java·服务器·算法
曾几何时`3 小时前
归并排序(一)
数据结构·算法·leetcode
CoovallyAIHub4 小时前
何必先OCR再LLM?视觉语言模型直接读图,让百页长文档信息不丢失
深度学习·算法·计算机视觉