有一个正整数数组 arr,现给你一个对应的查询数组 queries,其中 queries[i] = [Li, Ri]。
对于每个查询 i,请你计算从 Li 到 Ri 的 XOR 值(即 arr[Li] xor arr[Li+1] xor ... xor arr[Ri])作为本次查询的结果。
并返回一个包含给定查询 queries 所有结果的数组。
示例 1:
输入:arr = [1,3,4,8], queries = [[0,1],[1,2],[0,3],[3,3]]
输出:[2,7,14,8]
解释:
数组中元素的二进制表示形式是:
1 = 0001
3 = 0011
4 = 0100
8 = 1000
查询的 XOR 值为:
[0,1] = 1 xor 3 = 2
[1,2] = 3 xor 4 = 7
[0,3] = 1 xor 3 xor 4 xor 8 = 14
[3,3] = 8
示例 2:
输入:arr = [4,8,2,10], queries = [[2,3],[1,3],[0,0],[0,3]]
输出:[8,0,4,4]
提示:
- 1 <= arr.length <= 3 * 10^4
- 1 <= arr[i] <= 10^9
- 1 <= queries.length <= 3 * 10^4
- queries[i].length == 2
- 0 <= queries[i][0] <= queries[i][1] < arr.length
涉及到的知识点:
前缀和
异或的两个基本性质,a^a=0,a^0=a
思路:
1. 首先计算数组 `arr` 的前缀异或值并存储在 `pre` 数组中,通过遍历数组依次计算每个位置的前缀异或。 2. 然后对于每个查询 `queries` 中的一对起始索引和结束索引: - 如果起始索引为 `0`,则直接将结束索引对应位置的前缀异或值作为结果。 - 如果起始索引不为 `0`,则通过结束索引处的前缀异或值与起始索引减 `1` 处的前缀异或值进行异或运算,得到该查询区间的异或结果。 3. 将每次计算得到的结果添加到 `ans` 数组中,最后返回 `ans` 数组,即所有查询的结果。 总体来说,利用前缀异或的特性高效地处理了对数组不同区间进行异或计算的问题。
前缀和运算
前缀异或运算是一种对数组进行的运算,它的结果是一个新的数组,其中每个元素都是原数组对应位置之前的所有元素的异或值。 具体来说,假设原数组为$a_1,a_2,a_3,,那么前缀异或运算的结果数组$b_1,b_2,b_3,满足以下条件:
的主要作用是方便地计算数组的某些子段的异或值。通过前缀异或运算,可以在O(1)的时间复杂度内计算出任意子段的异或值,而不需要重新对该子段进行异或运算。 例如,对于数组a_1,a_2,a_3,a_4,a_5,a_6,其前缀异或数组为b_1,b_2,b_3,b_4,b_5,b_6。如果要计算子段a_3,a_4,a_5的异或值,可以通过$b_5\oplus b_2$来得到,因为b_5是a_1\oplus a_2\oplus a_3\oplus a_4\oplus a_5的异或值,b_2是a_1\oplus a_2的异或值,所以b_5\oplus b_2就是a_3\oplus a_4\oplus a_5的异或值。 前缀异或运算在一些算法和数据结构中经常被使用,例如在解决异或相关的问题、快速计算子段异或值等方面都有应用。
代码如下
class Solution(object): def xorQueries(self, arr, queries): """ :type arr: List[int] :type queries: List[List[int]] :rtype: List[int] """ n = len(arr) # 获取数组长度 pre = [0] * n # 初始化前缀异或结果数组 pre[0] = arr[0] # 第一个元素的前缀异或就是它本身 for i in range(1, n): # 从第二个元素开始计算前缀异或 pre[i] = pre[i - 1] ^ arr[i] print("pre=", pre) # 打印前缀异或结果 ans = [] # 用于存储最终结果的列表 for i in queries: # 遍历查询列表 if i[0] == 0: # 如果起始索引为 0 t = pre[i[1]] # 直接取对应位置的前缀异或结果 else: t = pre[i[1]] ^ pre[i[0] - 1] # 否则计算两个前缀异或结果的异或 # print(f"i[1]={i[1]},i[0]={i[0]}") # print(f"pre[i[1]]={pre[i[1]]} ,pre[i[0]]={pre[i[0]]}") ans.append(t) # 将计算结果添加到结果列表 return ans # 返回最终结果列表
时间复杂度:
- 计算前缀异或的过程需要遍历数组一次,时间复杂度为 O(n)。
- 对于每个查询,计算结果的操作时间复杂度为 O(1),因为只涉及到常数次基本运算。而查询的数量为 m(假设 queries 的长度为 m),所以处理所有查询的总时间复杂度为 O(m)。综合起来,总的时间复杂度为 O(n + m)。
空间复杂度:
- 需要额外创建一个长度为 n 的前缀异或数组 pre,所以空间复杂度为 O(n)。