题目大意
给定一个非空数组,数组中元素为 a0, a1, a2, … , an-1,其中 0 ≤ ai < 231 。
找到 ai 和aj 最大的异或 (XOR) 运算结果,其中0 ≤ i, j < n 。
你能在O(n)的时间解决这个问题吗?
示例:
输入: [3, 10, 5, 25, 2, 8]
输出: 28
解释: 最大的结果是 5 ^ 25 = 28.
解题思路
构建一个字典树(深度是31,即二进制数字的最高位数),首先遍历数组,生成该字典树。生成方式:对于当前数字num,从高位向低位计算,如果(num>>i)&1=true,表示该位置是1,生成右子树,反之生成左子树。
注:遍历数组,时间为O(N),每个数字生成字典树是常量时间(一共就31位),因此生成字典树的时间复杂度为O(N)。
字典树生成后,遍历数组;对于当前数字num,仍然从高位向低位计算(因为我们想让异或结果最大, 因此应该从高位开始比较)。
- (num>>i)&1=true表示num在位置i位置是1,为了让异或结果最大,我们应该在字典树中找一下,当前root中是否存在左子树(表示某个数字在该位置上为0),如果存在左子树,则将当前结果+(1<<i)(表示i位置异或之后是1),且root=root->left。如果不存在左子树,则直接root=root->right。
- (num>>i)&1=false,与上述操作相反即可。
注:字典树是根据整个数组生成的,因此root在字典树中移动的时候,对应的是数组中的某个数字,符合题意;
// 字典树
struct TrieRoot
{
TrieRoot * left;
TrieRoot * right;
};
class Solution {
private:
// 将某个数字插入字典树中
void insert(TrieRoot* root, int num){
for (int i = 30; i >= 0; --i){
if ((num >> i) & 1){
if (!root->right)
root->right = new TrieRoot;
root = root->right;
}
else{
if (!root->left)
root->left = new TrieRoot;
root = root->left;
}
}
}
public:
int findMaximumXOR(vector<int>& nums) {
TrieRoot * root = new TrieRoot;
// 将数组元素插入字典树
for (int i = 0; i < nums.size(); ++i){
insert(root, nums[i]);
}
int res = 0, tmp = 0;
TrieRoot * curNode = root;
// 计算每个数字在数组中的最大异或值
for (int i = 0; i < nums.size() ++i){
tmp = 0;
curNode = root;
// 从高位向低位计算,保证异或结果最大
for (int j = 30; j >= 0 --j){
if ((nums[i] >> j) & 1){
if (curNode->left){
tmp += (1 << j);
curNode = curNode->left;
}
else
curNode = curNode->right;
}
else{
if (curNode->right){
tmp += (1 << j);
curNode = curNode->right;
}
else
curNode = curNode->left;
}
}
res = max(res, tmp);
}
return res;
}
};