LeetCode 421 - 数组中两个数的最大异或值


文章目录
- 摘要
- 描述
- 题解答案
- 题解代码分析
- 代码逐步解析
- ① TrieNode 定义
- ② 插入数字到 Trie
- ③ 查找最大异或结果
- ④ 遍历所有数字求最大值
- 示例测试及结果
- 时间复杂度
- 空间复杂度
- 总结
摘要
这题的名字叫「数组中两个数的最大异或值」,看起来像是个普通的数学题,
但实际上这是个经典的位运算 + 前缀树优化问题。
直观暴力法就是两两异或一遍再取最大值,
但那样是 O(n²) 的复杂度,数组长度一大直接炸掉。
而高效做法是用「Trie(前缀树)」按二进制位去存每个数,
然后每次在树里找一个最能让当前数异或结果更大的路径,
最后就能在 O(32n) 的复杂度内解决问题。
本文我们会用 Swift 实现一个完整的解法,并带上运行示例与实战分析。

描述
题目要求:
给定一个整数数组
nums,返回数组中任意两个数nums[i] XOR nums[j]的最大值。
其中异或运算(^)的规则是:
- 相同为 0,不同为 1。
举个例子
5 (0101) 和 25 (11001) 的异或是:
0101
11001
-----
11100 (28)
结果就是 28。
所以题目等价于:
找到一对数字,它们在二进制每个位上尽量不同。
题解答案
这题有两种常见思路
暴力法
两层循环,枚举所有 (i, j) 组合,取最大 nums[i] ^ nums[j]。
复杂度 O(n²),当 n = 2×10⁵ 时,完全不可行。
字典树法(Trie)
- 把每个数字的二进制表示插入到一棵二叉 Trie 树中(0/1 分支);
- 对每个数,从高位到低位尝试在 Trie 中找「相反位」;
- 这样就能最大化异或结果。
我们选择第二种方法,效率高又优雅。

题解代码分析
下面是完整的 Swift 版本(可以直接在 Xcode Playground 运行)
import Foundationclass TrieNode {var children: [Int: TrieNode] = [:]
}class Solution {func findMaximumXOR(_ nums: [Int]) -> Int {let root = TrieNode()// 将数字插入Triefunc insert(_ num: Int) {var node = rootfor i in stride(from: 31, through: 0, by: -1) {let bit = (num >> i) & 1if node.children[bit] == nil {node.children[bit] = TrieNode()}node = node.children[bit]!}}// 查找最大异或func query(_ num: Int) -> Int {var node = rootvar xorValue = 0for i in stride(from: 31, through: 0, by: -1) {let bit = (num >> i) & 1// 想异或最大,就尽量找相反位let toggledBit = 1 - bitif let next = node.children[toggledBit] {xorValue |= (1 << i)node = next} else if let next = node.children[bit] {node = next}}return xorValue}// 主流程var maxXor = 0for num in nums {insert(num)}for num in nums {maxXor = max(maxXor, query(num))}return maxXor}
}
代码逐步解析
① TrieNode 定义
class TrieNode {var children: [Int: TrieNode] = [:]
}
每个节点最多有两个分支:
children[0]表示当前位是 0children[1]表示当前位是 1
② 插入数字到 Trie
for i in stride(from: 31, through: 0, by: -1) {let bit = (num >> i) & 1...
}
我们把每个数的 32 位二进制从高到低依次插入,
确保 Trie 里保留所有数字的二进制路径。
举例:插入 5 (000...0101) 时:
root└─0└─0...└─1└─0└─1
③ 查找最大异或结果
let toggledBit = 1 - bit
if let next = node.children[toggledBit] {xorValue |= (1 << i)node = next
}
遍历当前数字时,
- 如果能走相反的分支(
1-bit),就走那边(这样该位异或能得到 1); - 否则只能走相同的分支。
这就是让异或值尽量变大的贪心策略。
④ 遍历所有数字求最大值
for num in nums {maxXor = max(maxXor, query(num))
}
对每个数字都查一遍,找到能和它异或最大的结果。
示例测试及结果
我们来跑几个示例
let solution = Solution()
print(solution.findMaximumXOR([3,10,5,25,2,8])) // 输出: 28
print(solution.findMaximumXOR([14,70,53,83,49,91,36,80,92,51,66,70])) // 输出: 127
print(solution.findMaximumXOR([0])) // 输出: 0
print(solution.findMaximumXOR([2,4])) // 输出: 6
输出结果
28
127
0
6
可以看到完全符合题目预期。
例如 [3,10,5,25,2,8] 中,5 ^ 25 = 28 确实是最大异或值。
时间复杂度
- 构建 Trie:O(n × 32)
- 查询每个数最大异或值:O(n × 32)
总时间复杂度:O(32n) ≈ O(n)
空间复杂度
Trie 树的节点最多为 32 × n 个。
空间复杂度:O(32n) ≈ O(n)
总结
这道题是「位运算 + Trie 树」的经典题型。
它的核心思想是:
利用二进制前缀树在每一位上做“相反匹配”,
让异或的每一位都尽量为 1,从而得到最大值。
在实际开发中,这种思想常用于:
- 网络掩码匹配(IP 地址的前缀匹配);
- 路由查找;
- 加密或压缩算法中位优化。
Swift 实现虽然看起来略繁琐,但逻辑其实非常清晰:
结构体化思维 + 位运算配合字典树,是一种非常高效且优雅的解法。
