421. 数组中两个数的最大异或值
421. 数组中两个数的最大异或值
难度中等
给你一个整数数组 nums
,返回 nums[i] XOR nums[j]
的最大运算结果,其中 0 ≤ i ≤ j < n
。
**进阶:**你可以在 O(n)
的时间解决这个问题吗?
示例 1:
输入:nums = [3,10,5,25,2,8]
输出:28
解释:最大运算结果是 5 XOR 25 = 28.
示例 2:
输入:nums = [0]
输出:0
示例 3:
输入:nums = [2,4]
输出:6
示例 4:
输入:nums = [8,10,2]
输出:10
示例 5:
输入:nums = [14,70,53,83,49,91,36,80,92,51,66,70]
输出:127
提示:
1 <= nums.length <= 2 * 10^4
0 <= nums[i] <= 2^31 - 1
函数签名:
func findMaximumXOR(nums []int) int
分析
每次遍历到 nums[i]
,可以假设这个数字是构成结果的一个元素,已经遍历过的数字里挑一个和当前数字亦或运算,这样的话是 O(n^2)
的复杂度。
两层循环的朴素解法显然不是这个问题的目的,怎么降低复杂度呢?根据提示只需遍历一遍数组就能得出结果,这是怎么做到的?
可以用前缀树的技巧把已经遍历的数字记录起来。具体来说,把每个数字看做二进制,根据题目限制,最多30位,将其插入前缀树里,前缀树的深度最多为30。
用当前数字和之前的数字亦或时,尽量在高位取得 1 即可。
这里的前缀树每个节点最多 2 个孩子节点,是一棵简单二叉树。
type Trie struct {
zero, one *Trie
}
const bitLimt = 30
func (t *Trie) insert(x int) {
cur := t
for i := bitLimt; i >= 0; i-- {
b := (x >> i) & 1
if b == 0 {
if cur.zero == nil {
cur.zero = &Trie{}
}
cur = cur.zero
} else {
if cur.one == nil {
cur.one = &Trie{}
}
cur = cur.one
}
}
}
func (t *Trie) check(x int) int {
res := 0
cur := t
for i := bitLimt; i >= 0; i-- {
res <<= 1
b := (x >> i) & 1
if b == 0 {
if cur.one != nil {
cur = cur.one
res++
} else {
cur = cur.zero
}
} else {
if cur.zero != nil {
cur = cur.zero
res++
} else {
cur = cur.one
}
}
}
return res
}
func findMaximumXOR(nums []int) int {
res := 0
root := &Trie{}
for i := 1; i < len(nums); i++ {
root.insert(nums[i-1])
res = max(res, root.check(nums[i]))
}
return res
}
时空复杂度都是 O(n*30) = O(n)
。