1. 概述

在本教程中,我们将探讨如何高效计算一个数组中指定区间元素的异或(XOR)结果。

首先,我们会介绍直接遍历区间的朴素方法。接着,我们会通过前缀异或(Prefix XOR)技巧来优化查询效率。最后,我们会对两种方法进行比较,帮助你理解它们的适用场景和性能差异。


2. 问题定义

我们给定一个长度为 n 的数组 A 和若干次查询请求。每次查询要求我们计算区间 [L, R] 内所有元素的异或值,即:

A[L] XOR A[L+1] XOR A[L+2] XOR ... XOR A[R]

数组内容在查询过程中保持不变,也就是说,没有更新操作。


3. 异或基础知识

异或(XOR)是一种按位运算。其运算规则如下:

A B A XOR B
0 0 0
0 1 1
1 0 1
1 1 0

从表中可以看出:

  • 若两个位相同,结果为 0;
  • 若两个位不同,结果为 1。

关键性质

  • 异或满足交换律和结合律;
  • 任何数异或 0 仍等于它本身;
  • 任何数异或自身等于 0。

⚠️ 一个重要的点是:每个 bit 的异或运算互不干扰。也就是说,最终结果中每一位的值只取决于该位在所有操作数中 1 的个数奇偶性。


4. 朴素解法

算法思路

直接遍历 [L, R] 区间,逐个异或所有元素,得到最终结果。

示例代码

int naiveXorRange(int[] A, int L, int R) {
    int answer = 0;
    for (int i = L; i <= R; i++) {
        answer ^= A[i];
    }
    return answer;
}

时间复杂度

  • 预处理:无
  • 单次查询复杂度O(n),最坏情况下需要遍历整个数组

📌 适用于查询次数少、数组较小的场景。


5. 前缀异或优化方法

5.1 核心思想

我们预先计算一个前缀异或数组 prefix,其中:

prefix[i] = A[0] XOR A[1] XOR ... XOR A[i - 1]

这样,当我们需要查询 [L, R] 区间的异或时,可以通过以下公式快速计算:

A[L] XOR A[L+1] XOR ... XOR A[R] = prefix[R + 1] XOR prefix[L]

为什么成立?

因为异或具有自反性,prefix[R + 1] XOR prefix[L] 实际上是将 [0, L) 区间的异或抵消,只保留 [L, R] 区间的异或结果。


5.2 预处理算法

int[] buildPrefixXor(int[] A) {
    int n = A.length;
    int[] prefix = new int[n + 1];
    for (int i = 1; i <= n; i++) {
        prefix[i] = prefix[i - 1] ^ A[i - 1];
    }
    return prefix;
}

时间复杂度

  • 预处理复杂度O(n)

5.3 查询算法

int query(int L, int R, int[] prefix) {
    return prefix[R + 1] ^ prefix[L];
}

时间复杂度

  • 单次查询复杂度O(1)

6. 方法对比

方法 预处理复杂度 查询复杂度 是否支持更新 适用场景
朴素解法 O(n) 查询少、数组小
前缀异或法 O(n) O(1) 查询频繁、数组大

📌 结论

  • 如果你只需要处理少量查询或数组频繁更新,直接使用朴素方法更简单;
  • 如果你需要处理大量查询且数组固定不变,前缀异或法效率更高。

7. 总结

本文介绍了两种求数组区间异或和的方法:

  • 朴素方法:简单直观,适合小规模或频繁更新的场景;
  • 前缀异或法:通过预处理换取查询效率的大幅提升,适合大规模、固定数组的场景。

掌握异或的性质以及前缀异或的构造方式,有助于你在处理类似问题时写出更高效的代码。


原始标题:Finding XOR of All Numbers in a Given Range