原题链接
给你一个整数数组 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 * 104
0 <= nums[i] <= 231 - 1
Related Topics 位运算 字典树 数组 哈希表
364 0
class Solution {
public int findMaximumXOR(int[] nums) {
int ans = 0;
for (int i = 0; i < nums.length; i++) {
for (int j = 0; j < nums.length; j++) {
ans = Math.max(ans, nums[i] ^ nums[j]);
}
}
return ans;
}
}
构建二进制前缀树
具体来说就是利用数的二进制表示,从高位到低位构建一棵树(因为只有0和1 两个值,所以是一棵二叉树),每个从根节点到叶子节点的路径都表示一个数。(构建的树看下图)
搜索前缀树
然后遍历数组中的数字,将每一个二进制位,在对应的层中找到一个异或的最大值,也就是:如果是1,找0的那条路径,如果是0,找1的那条路径。
这样搜索下来的路径就是这个数字和整个数组异或的最大值,如下图
class Solution {
// son[0] 代表 0, son[1] 代表 1
class Node {
Node[] son = new Node[2];
}
// 字典树的根节点
Node root = new Node();
public int findMaximumXOR(int[] nums) {
int res = 0;
for (int x : nums) insert(x);
for (int x : nums) {
res = Math.max(res, search(x));
}
return res;
}
void insert(int x) {
Node p = root;
// 最高位的二进制位编号为 30
for (int i = 30; i >= 0; i--) {
// 取第 i 位数字
int t = (x >> i) & 1;
if (p.son[t] == null) {
p.son[t] = new Node();
}
p = p.son[t];
}
}
int search(int x) {
Node p = root;
int res = 0;
for (int i = 30; i >= 0; i--) {
int t = (x >> i) & 1;
if (t == 0) {
// 第i位二进制位为0, 若要找最大应此时需要找 1 的结点
if (p.son[1] != null) {
p = p.son[1];
res = res * 2 + 1;
} else {
p = p.son[0];
res = res * 2;
}
} else {
// 第i位二进制位为1, 若要找最大应此时需要找 0 的结点
if (p.son[0] != null) {
p = p.son[0];
res = res * 2 + 1;
} else {
p = p.son[1];
res = res * 2;
}
}
}
return res;
}
}
参考:
Python3 巧妙利用前缀树剪枝,复杂度 O(N)
宫水三叶の相信科学系列】详解为何能用「贪心」&「Trie」找「最大异或结果」
由于只有由于字典树中的每个节点最多只有两个子节点,分别表示 0 和 1,因此本题中的字典树是一棵二叉树。在设计字典树的数据结构时,我们可以令左子节点 left 表示 0,右子节点 right 表示 1
class Solution {
class Node {
// 左子树指向表示 0 的子节点
Node left;
// 右子树指向表示 1 的子节点
Node right;
}
// 字典树的根节点
Node root = new Node();
public int findMaximumXOR(int[] nums) {
// 构建字典树
for (int x : nums) {
add(x);
}
int ans = 0;
// 遍历查找最大值
for (int x : nums) {
ans = Math.max(ans, search(x));
}
return ans;
}
// 将一个数添加到字典树中
public void add(int x) {
Node cur = root;
for (int i = 30; i >= 0; i--) {
int bit = (x >> i) & 1;
if (bit == 0) {
if (cur.left == null) {
cur.left = new Node();
}
// 已经存在结点, 则向下推进
cur = cur.left;
} else {
if (cur.right == null) {
cur.right = new Node();
}
cur = cur.right;
}
}
}
// 查找与 x 异或的最大值
public int search(int x) {
int res = 0;
Node cur = root;
for (int i = 30; i >= 0; i--) {
int bit = (x >> i) & 1;
if (bit == 0) {
// x 的第 i 个二进制位为 0,应当往表示 1 的子节点 right 走
if (cur.right != null) {
res = res * 2 + 1;
cur = cur.right;
} else {
res = res * 2;
cur = cur.left;
}
} else {
// x 的第 i 个二进制位为 1,应当往表示 0 的子节点 left 走
if (cur.left != null) {
res = res * 2 + 1;
cur = cur.left;
} else {
res = res * 2;
cur = cur.right;
}
}
}
return res;
}
}
参考: 官方题解