LeetCode 421 数组中两个数的最大异或值
题目链接: https://leetcode-cn.com/problems/maximum-xor-of-two-numbers-in-an-array
这道题要找出给定列表中两个元素的最大异或值,这里采用字典树来写。
穷举法
要求给定列表任意两个元素的最大异或值,如果暴力做法,可以直接穷举两层列表中的各个元素。初始化结果为0,不断求得元素i和j的异或值,如果大于当前结果则更新当前结果为i,j的异或值。代码如下:
class Solution {
public:
int findMaximumXOR(vector<int>& nums) {
int maxXOR = 0;
for(int i = 0; i < nums.size(); i++){
for(int j = 0; j < nums.size(); j++){
maxXOR = max(maxXOR, nums[i] ^ nums[j]);
}
}
return maxXOR;
}
};
这个做法两层遍历列表,其时间复杂度为O(N^2)
,空间复杂度为O(N)
。显然,对于规模较大的数组,O(N^2)
时间复杂度的算法是比较低效的,在这里运行数据会超时。
如何优化
不管怎样我们都需要遍历一层数组,确定nums[i]
,在这里我们没有办法优化。但是对于内层循环,我们应该怎样优化呢?研究异或运算的性质得到,同1同0异或为0,一1一0异或才为1。由于每个二进制数的高位权值较大,所以对于给定的nums[i]
,我们只需要找出nums[j]
,它满足在nums
中他的高位尽可能和nums[i]
的高位不同,这样他们高位上异或得到一,结果也就更大。
在这里由于涉及到位运算,而二进制数是由给定位串组成的。对于给定范围数字都是31位整数,可以先建一棵trie
,然后对于列表每个元素不进行整个列表的遍历搜索,而是搜索这棵树。这样用trie
把O(N)
的内层循环优化到常数级别,因为可以把遍历整个长度为N的数组的过程变为遍历一个深度为31的trie
树的过程。
从根节点向下应该是高位到低位
代码如下:
/**
trie树节点
**/
class TrieNode{
public:
TrieNode* children[2] = {NULL, NULL};
};
/**
trie树
**/
class Trie{
public:
TrieNode* root = new TrieNode();
/**
向trie中插入一个数,按从root高位->低位存储
**/
void insert(int num){
TrieNode* now = root;
// 倒序确保从高位到低位优先
for(int i = 30; i >= 0; i--){
int tmp = num >> i & 1; // 数的二进制形式第i位值
if(now->children[tmp] == NULL){ // 如果对应位的节点不存在
now->children[tmp] = new TrieNode();
}
now = now->children[tmp];
}
}
/**
在trie中查找num的xor最大值
**/
int query(int num){
TrieNode* now = root;
int res = 0, tmp;
for(int i = 30; i >= 0; i--){
tmp = num >> i & 1;
if(now->children[!tmp]){
res += 1 << i;
tmp = !tmp;
}
now = now->children[tmp];
}
return res;
}
};
class Solution {
public:
int findMaximumXOR(vector<int>& nums) {
Trie trie;
// 建立trie树
for(int i = 0; i < nums.size(); i++){
trie.insert(nums[i]);
}
// 在trie树中找出各个元素的最大匹配并更新结果
int maxXOR = 0;
for(int i = 0; i < nums.size(); i++){
maxXOR = max(maxXOR, trie.query(nums[i]));
}
return maxXOR;
}
};