目录
一、题目描述
给定一个非空数组,数组中元素为 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.
二、解题思路
这题O(n^2)的想法很容易,但是超时也是绝对的。超时的原因在于,我们需要把每个数依次跟其余所有数进行异或操作才能得到最大值。
如果有一种方法,能让我们每个数可以同时跟剩余所有的数同时进行异或操作,那时间复杂度不就是一次遍历就可以了吗?
顺着这个想法可以想到字典树,字典树又叫前缀树。先把每个数字按照二进制位构建字典树(此时字典树是一棵二叉树),建树示意图如下,以下图片均来自力扣官方题解;
然后把数组中的每个数同样按照二进制位依次去匹配,匹配的过程如下:
因为结果是求最大的异或值,所以:
- 如果该数的当前位是1,若存在0,则选择0异或;
- 如果该数的当前位是0,若存在1,则选择1异或。
另:题解中有大佬利用异或的性质(a^b=c, a^c=b, b^c=a)做了另一种解法,同样很巧妙,值得学习。这里贴出链接。
三、代码实现
#include <bits/stdc++.h>
using namespace std;
class node {
public:
//只有0和1两个分支
node* TrieNode[2];
node() { memset(TrieNode, 0, sizeof(TrieNode)); }
~node() {
for (int i = 0; i < 2; i++) {
delete TrieNode[i];
}
}
};
class Solution {
public:
node* Root;
Solution() { Root = new node(); }
~Solution() { delete Root; }
int findMaximumXOR(vector<int>& nums) {
int n = nums.size();
int res = 0;
//构建字典树
//把每个数字按照位插入进字典树
for (int i = 0; i < n; i++) {
node* p = Root;
for (int j = 31; j >= 0; j--) {
int bit = nums[i] >> j & 1;
if (!p->TrieNode[bit]) {
p->TrieNode[bit] = new node();
}
p = p->TrieNode[bit];
}
}
//分别拿每个数去跟字典树贪心的进行异或操作
for (int i = 0; i < n; i++)
{
node* p = Root;
int tmp = 0;
for (int j = 31; j >= 0; j--) {
int bit = nums[i] >> j & 1;
//如果该数的当前位是1,那么为了异或的结果最大,必须去寻找0来跟他异或
//如果该数的当前位是0,那么为了异或的结果最大,必须去寻找1来跟他异或
if (bit == 1) {
tmp += p->TrieNode[0] ? 1 << j : 0 ;
p = p->TrieNode[0] ? p->TrieNode[0] : p->TrieNode[1] ;
}else{
tmp += p->TrieNode[1] ? 1 << j : 0 ;
p = p->TrieNode[1] ? p->TrieNode[1] : p->TrieNode[0] ;
}
}
res = max(res, tmp);
}
return res;
}
};
int main() {
vector<int> nums = {3, 10, 5, 25, 2, 8};
Solution *s = new Solution();
cout << s->findMaximumXOR(nums);
return 0;
}