数组中两个数的最大异或值 图解题解
这道题到底在问什么
- 输入
- nums = [3,10,5,25,2,8]
- 输出
- 28(5 XOR 25)
- 输入
- nums = [3,5,6]
- 输出
- 6(3 XOR 5)
最优解:一步一步想明白
- 3记住「高位优先、能相反就相反」:异或的高位拉开差距最划算,所以每个数都尽量去匹配一个高位与它相反的数。下面先建树、再查询,逐帧套它。
- 4插入 3:第 1 位 0 → 节点 0
- 5插入 3:第 2 位 1 → 节点 01
- 6插入 3:第 3 位 1 → 节点 011
- 7插入 5:第 1 位 1 → 节点 1
- 8插入 5:第 2 位 0 → 节点 10
- 9插入 5:第 3 位 1 → 节点 101
- 10插入 6:第 1 位 1 → 节点 1
- 11插入 6:第 2 位 1 → 节点 11
- 12插入 6:第 3 位 0 → 节点 110
- 13字典树建好:3 个数各走出一条根到叶的路径
- 14查询 3:第 1 位想走 1 → 成功,这位记 1
- 15查询 3:第 2 位想走 0 → 成功,这位记 1
- 16查询 3:第 3 位想走 0 → 失败,这位记 0
- 17数 3 的最佳异或 = 6;ans = 6
- 18查询 5:第 1 位想走 0 → 成功,这位记 1
- 19查询 5:第 2 位想走 1 → 成功,这位记 1
- 20查询 5:第 3 位想走 0 → 失败,这位记 0
- 21数 5 的最佳异或 = 6;ans = 6
- 22查询 6:第 1 位想走 0 → 成功,这位记 1
- 23查询 6:第 2 位想走 0 → 失败,这位记 0
- 24查询 6:第 3 位想走 1 → 成功,这位记 1
- 25数 6 的最佳异或 = 5;ans = 6
- 26答案:最大异或 = 6
⚠️ 容易写错的地方
✗ 错:从低位开始贪心
✓ 对:一定从最高位开始
高位的 1 比低位所有位加起来还大,必须优先把高位的异或抢成 1
✗ 错:所有数补到一样长前忘了对齐位宽
✓ 对:统一按固定位宽(如 32 位)取每一位
不同数二进制长度不同,不对齐高位会让比较错位
✗ 错:查询时找不到相反分支就报错
✓ 对:相反分支为空时退而走相同分支、这位记 0
相同位异或得 0,路要继续往下走,不能中断
完整代码(Python / C++ / Java)
Python
from typing import List
class Solution:
def findMaximumXOR(self, nums: List[int]) -> int:
root = {}
for x in nums:
node = root
for b in range(31, -1, -1):
bit = (x >> b) & 1
node = node.setdefault(bit, {})
ans = 0
for x in nums:
node = root
cur = 0
for b in range(31, -1, -1):
bit = (x >> b) & 1
want = bit ^ 1
if want in node:
cur |= 1 << b
node = node[want]
else:
node = node[bit]
ans = max(ans, cur)
return ansC++
#include <vector>
using namespace std;
class Solution {
struct Node { int child[2] = {-1, -1}; };
public:
int findMaximumXOR(vector<int>& nums) {
vector<Node> trie(1);
for (int x : nums) {
int p = 0;
for (int b = 31; b >= 0; --b) {
int bit = (x >> b) & 1;
if (trie[p].child[bit] == -1) { trie[p].child[bit] = trie.size(); trie.push_back(Node()); }
p = trie[p].child[bit];
}
}
int ans = 0;
for (int x : nums) {
int p = 0, cur = 0;
for (int b = 31; b >= 0; --b) {
int bit = (x >> b) & 1, want = bit ^ 1;
if (trie[p].child[want] != -1) { cur |= 1 << b; p = trie[p].child[want]; }
else p = trie[p].child[bit];
}
ans = max(ans, cur);
}
return ans;
}
};Java
import java.util.*;
class Solution {
static class Node { Node[] child = new Node[2]; }
public int findMaximumXOR(int[] nums) {
Node root = new Node();
for (int x : nums) {
Node p = root;
for (int b = 31; b >= 0; b--) {
int bit = (x >>> b) & 1;
if (p.child[bit] == null) p.child[bit] = new Node();
p = p.child[bit];
}
}
int ans = 0;
for (int x : nums) {
Node p = root;
int cur = 0;
for (int b = 31; b >= 0; b--) {
int bit = (x >>> b) & 1, want = bit ^ 1;
if (p.child[want] != null) { cur |= 1 << b; p = p.child[want]; }
else p = p.child[bit];
}
ans = Math.max(ans, cur);
}
return ans;
}
}复杂度
时间
O(32n)
建树:n 个数各走 32 位;查询:n 个数各走 32 位。位宽 32 是常数,整体相当于 O(n)
空间
O(32n)
字典树最多 32n 个节点(每个数最坏新建一条 32 层的路径)
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 数组中两个数的最大异或值 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
除了字典树,还有别的解法吗?+
有。可以用「逐位 + 哈希集合」:从高位到低位逐位确定答案,假设当前答案这一位能是 1,把所有数的高位前缀放进哈希集合,检查是否存在两个前缀异或等于「期望答案」,存在就保留这一位。复杂度同样是 O(32n),思路也是高位贪心,只是把字典树换成了哈希前缀。
为什么时间能做到接近线性?+
因为整数位宽是固定的常数(本题 32 位)。每个数无论建树还是查询,都只走固定的 32 步,所以是 O(32n),把字面上的两两枚举 O(n²) 降到了线性级别。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 数组中两个数的最大异或值 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。