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
知识点&算法
因为异或的时候需要逐位比较,而数字的二进制表示可能长短不一,所以将他们统一储存为31位二进制数(因为数据范围是0 - 2 ^ 31 - 1)。
然后枚举数,和其他的数进行异或运算,取最大异或值。
用字典树对已有的数据进行维护。
对于当前枚举的数,从高位开始,贪心地尽可能选取相反的位值,如果字典树中已有的数无法满足取相反位,就取相同位。
题解
class Solution {
public:
struct trie{
struct trie* nextDigit[2];
trie(){
memset(nextDigit,0,sizeof(nextDigit));
}
};
int shiftLimit = 30;
trie* root = new trie;
trie* node;
void insert(int num){
node = root;
for(int i = shiftLimit ; i >= 0 ; --i){
int digit = (num >> i) & 1;
if(node->nextDigit[digit] == nullptr) node->nextDigit[digit] = new trie;
node = node->nextDigit[digit];
}
return;
}
int search(int num){
node = root;
int res = 0;
for(int i = shiftLimit ; i >= 0 ; --i){
int digit = (num >> i) & 1;
if(digit == 0){
if(node->nextDigit[1]){
res = res * 2 + 1;
node = node->nextDigit[1];
}else{
res *= 2;
node = node->nextDigit[0];
}
}else{
if(node->nextDigit[0]){
res = res * 2 + 1;
node = node->nextDigit[0];
}else{
res *= 2;
node = node->nextDigit[1];
}
}
}
return res;
}
int findMaximumXOR(vector<int>& nums) {
int n = nums.size(),ans = 0;
for(int i = 0 ; i < n ; ++i) insert(nums[i]);
for(int i = 0 ; i < n ; ++i) ans = max(ans,search(nums[i]));
return ans;
}
};
最后主函数更新答案的部分可以只写一个循环,只匹配枚举的数和此前的数,枚举的数和此后的数相当于枚举到此后的数与之前的数,所以不会遗漏。
i = 1 ; i < n 相比 i = 0 ; i < n - 1可以节省每次循环一个运算的时间开销
for(int i = 1 ; i < n ; ++i){
insert(nums[i-1]);
ans = max(ans,search(nums[i]));
}