Leetcode 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 * 104
- 0 <= nums[i] <= 231 - 1
思路
最简单的方法是暴力枚举所有可能的两个数,但时间复杂度为O(n2);
另一种方法是贪心,我们可以这样想:要想让异或的值越大,就要尽量让两个数对应的每一位都不同(异或的规则是:相同为零,不同为1)
我们可以先把num[j]之前的数全都用一颗前缀树存起来,不包括num[j]。
在枚举num[j]时判断其二进制下的每一位,如果当前位为0,就查找Trie树中是否有在对应位置为1的数,如果没有,只能走0路径;
如果当前位为1,就查找Trie树中是否有在对应位置为0的数,如果没有,只能继续走1路径;
这样一直走到最底层,就可以找到与num[j]异或最大的值num[i],并求出最后的结果;
Trie树
Trie树的核心操作是插入和查询,如果是用循环控制递归的深度,那么不用设置是否为叶节点的标志;如果是直接递归,则需要添加一个标记是否为叶节点的标志;
代码
class Solution {
//字典树的根节点
private Trie root = new Trie();
//最大的二进制编号
static final int HIGH_BIT = 30;
public int findMaximumXOR(int[] nums) {
int res = 0;
//这里的循环并没有考虑nums[0]和nums[0]异或的情况,因为自身和自身异或一定为零
for(int i = 1; i < nums.length; i ++) {
//将该数字加入字典树
insert(nums[i - 1]);
//将nums[i]看做ai,找出最大的x更新答案
res = Math.max(res, check(nums[i]));
}
return res;
}
public void insert(int num) {
Trie cur = root;
//所有元素都是等长的
for(int k = HIGH_BIT; k >= 0; k --) {
int bit = (num >> k) & 1; //判断第i位是1还是0
if(bit == 0) {
//如果当前节点不存在,则新建一个
if(cur.left == null) cur.left = new Trie();
cur = cur.left;
}
else {
if(cur.right == null) cur.right = new Trie();
cur = cur.right;
}
}
}
public int check(int num) {
Trie cur = root;
int ans = 0;
for(int k = HIGH_BIT; k >= 0; k --) {
int bit = (num >> k) & 1;
//如果当前位是0, 则判断是否存在1的路径
if(bit == 0) {
if(cur.right != null) {
ans = ans * 2 + 1;
cur = cur.right;
} else {
ans = ans * 2;
cur = cur.left;
}
}
else {
if(cur.left != null) {
ans = ans * 2 + 1;
cur = cur.left;
} else {
ans = ans * 2;
cur = cur.right;
}
}
}
return ans;
}
}
//这里每个数都是固定32位长的,所以不用添加结束标志
class Trie {
Trie left = null;
Trie right = null;
}