题目描述
LeetCode上的421. 数组中两个数的最大异或值,难度:中等
给你一个整数数组 nums ,返回 nums[i] XOR nums[j] 的最大运算结果,其中 0 ≤ i ≤ j < n 。
示例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 <= nums.length <= 2 * 1050 <= nums[i] <= 231 - 1
字典树
首先来解释一下异或运算:「相同值异或结果为 0,不同值异或结果为 1」
我们确定nums[j](0<=j<nums.length),在[0,j]的范围找nums[i],是nums[i]^nums[j]最大,即两者的二进制的每一位尽量不相同。因此我们在遍历nums过程中,就将nums[j]的每一位从高位到低位存储到字典树中。然后从字典树中【字典树已经保存了下标属于[0,j]的nums元素了】找出与nums[j]异或最大的数【该数字一定是nums[0]~nums[j]中的一个】
那么字典树如何操作呢?
- 从高位到低位遍历(利用右移并与1相与拿到当前位的数字【0/1】),并依次添加到字典树中。
- 拿到与num异或的最大数字的方法
getVal()- 如果该位为1,我们应当向0的方向前进,若0方向不存在,只能向1的方向前进
- 如果该位为0,我们应当向0的方向前进,若1方向不存在,只能向0的方向前进
class Trie {
Trie[] next = new Trie[2];
}
class Solution {
Trie root = new Trie();
public int findMaximumXOR(int[] nums) {
int ans = 0;
for(int num : nums) {
add(num);
ans = Math.max(ans, num ^ getVal(num));
}
return ans;
}
public void add(int num) {
Trie node = root;
// 固定4个字节,即32位,题目给出
for(int i = 31; i >= 0; i--) {
// 拿到当前i位置的元素0/1
int curEle = (num >> i) & 1;
if(node.next[curEle] == null) {
node.next[curEle] = new Trie();
}
node = node.next[curEle];
}
}
public int getVal(int num) {
Trie node = root;
int ans = 0;
// 遍历的方向是从高位到低位,因此在计算ans的时候需要注意
for(int i = 31; i >= 0; i--) {
// 拿到当前i位置的元素
int curEle = (num >> i) & 1;
int reverseEle = 1 - curEle;
// 下面根据当前位置的元素去字典树中判断是否有该元素,若没有就朝相反的方向前进
if(curEle == 0) {
// 最好朝着1的方向前进,若1方向为null,那就只能去0的方向了
// 别忘了计算ans
if(node.next[reverseEle] != null) {
node = node.next[reverseEle];
// +改为或‘|’速度会有提升
ans += (1 << i);
} else {
node = node.next[curEle];
}
} else {
// 当前元素位1,最好朝着0的方向前进,若0方向为null,那就只能朝着1的方向前进了
if(node.next[reverseEle] != null) {
node = node.next[reverseEle];
} else {
node = node.next[curEle];
ans += (1 << i);
}
}
}
// 将与num异或最大的数返回
return ans;
}
}