421 数组中两个数的最大异或值
题目:
给你一个整数数组 nums
,返回 nums[i] XOR nums[j]
的最大运算结果,其中 0 ≤ i ≤ j < n
。
提示:
1 <= nums.length <= 2 * 105
0 <= nums[i] <= 231 - 1
示例 1:
输入:nums = [3,10,5,25,2,8] 输出:28 解释:最大运算结果是 5 XOR 25 = 28.
示例 2:
输入:nums = [14,70,53,83,49,91,36,80,92,51,66,70] 输出:127
解题思路
异或,就是二进制位中不同为 1,相同为 0。要求两个数的异或值最大,那么这两个数的二进制位应该尽可能的不一样。
最朴素的解法是两层循环,依次计算。
更快的方法是字典树。
题目中已经说明数组中的值在区间[0, 2^31-1]
之间,所以我们可以按位处理每个值,存储在字典树中。
考虑到二进制中每位只可能是0或者1,所以字典树的节点可以用左右表示:
type Trie struct {
left, right *Trie // left for 0, right for 1
}
具体为:
把数组第一个元素加入字典树
从第二个元素x开始,先从字典树中查找与 x 异或的最大值,然后再把 x 也加入到字典树中。
遍历的时候,顺便保留最大的异或值即可。
如何从字典树中查找最大的异或值呢?
从字典树的根节点开始,如果元素 x 的当前位为 0,那么就往字典树的 right(即该位 为1)继续查找;否则,往字典树的 left 中查找。
// date 2024/01/26
type Trie struct {
left, right *Trie // left 0, right 1
}
func (t *Trie) Add(num int) {
cur := t
for i := 30; i >= 0; i-- {
bit := num >> i & 0x1
if bit == 0 {
// left 0
if cur.left == nil {
cur.left = &Trie{}
}
cur = cur.left
} else {
if cur.right == nil {
cur.right = &Trie{}
}
cur = cur.right
}
}
}
func (t *Trie) findMaxXORForNum(num int) int {
cur := t
ans := 0
for i := 30; i >= 0; i-- {
bit := num >> i & 0x1
ans = ans << 1
if bit == 0 {
// use the right of exist num
if cur.right != nil {
cur = cur.right
ans += 1
} else {
cur = cur.left
}
} else {
// use the left
if cur.left != nil {
cur = cur.left
ans += 1
} else {
cur = cur.right
}
}
}
return ans
}
func findMaximumXOR(nums []int) int {
ans := 0
tie := &Trie{}
for i, v := range nums {
if i == 0 {
tie.Add(v)
continue
}
res := tie.findMaxXORForNum(nums[i])
if res > ans {
ans = res
}
tie.Add(nums[i])
}
return ans
}
为什么一次遍历就可以?
因为异或满足交换律。
最后更新于