1、LCR 067. 数组中两个数的最大异或值
题目信息:
cpp
给定一个整数数组 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 = [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 * 105
0 <= nums[i] <= 231 - 1
进阶:你可以在 O(n) 的时间解决这个问题吗?
解题思路1:
- 1、审题:输入一个整数数组,要求对数组中的两个数字进行异或操作xor,并求出最终得到的异或值最大的数值并返回,
- 注意两个数字可以相同,所以异或值最小也比当前的遍历值要大
- 2、解题:暴力解法,遍历数组中的元素,两两进行异或xor操作,并最出最大值
代码实现1:
cpp
int findMaximumXOR1(vector<int> &nums)
{
int res = 0;
int size = nums.size();
for (int i = 0; i < size; i++)
{ // 两层for循环
int num1 = nums[i];
for (int j = i + 1; j < size; j++)
{
int num2 = nums[j];
int xorRes = num1 ^ num2;
res = max(res, xorRes);
}
}
return res;
}
解题思路2:
- 解法2:前缀树解法
- 将整数num转换成数位保存到前缀树中,数位是二进制,保存的数字不是0就是1,所以前缀树的子节点只有两个,
- 每个整数int类型为32位,如果是正整数,则最左边数位为0。
- 先将数组中每个int整数插入到前缀树中,根据他的数位值来创建节点,从最高位到最低位
- 查找两个数字的最大异或值,先遍历数字,然后查找与之对应的数位是否存在,如果存在则选中这个节点并且异或值增加1
- 如果没有说明这个数位位置存在相同的数字节点,异或值结果不能增加1,
- 问题的关键在于,查找异或值的过程中,下一个节点为异或值的目标节点,而异或值在最高位其值越高,
- 所以异或值的结点从最高位开始就要选择值不同的结点,然后往下一直选择不同值的结点,如果没有不同值结点则选中相同值结点,异或值结果位数值不增加只往右移动
代码实现2:
cpp
/**
* 位数前缀树
* - 只有两个节点,使用长度为2的数组保存节点
*/
class TrieNode
{
public:
TrieNode *childred[2];
TrieNode()
{
for (int i = 0; i < 2; i++)
{
this->childred[i] = nullptr;
}
}
~TrieNode()
{
for (int i = 0; i < 2; i++)
{
this->childred[i] = nullptr;
}
}
};
TrieNode *buildTrie(vector<int> &nums)
{
TrieNode *root = new TrieNode();
// 遍历数组,取出int数字,从最高位取出数位,然后插入到前缀树中
for (auto num : nums)
{
TrieNode *node = root;
for (int i = 31; i >= 0; --i)
{
int bit = (num >> i) & 1; // 数位的数字
if (node->childred[bit] == nullptr)
{
node->childred[bit] = new TrieNode();
}
node = node->childred[bit];
}
}
return root;
}
int getMaxXOR(vector<int> &nums, TrieNode *root)
{
int res = 0;
// 遍历数组中的整数,根据该整数的数位,从高位到低位,查找与之对应数位的数字节点是否存在,存在或不存在都从该结点的对应结点位置开始往下遍历
for (const auto num : nums)
{
TrieNode *node = root;
int xorRes = 0;
for (int i = 31; i >= 0; --i)
{
int bit = (num >> i) & 1;
// 查找与之相对的结点是否存在
if (node->childred[1 - bit] != nullptr)
{
xorRes = (xorRes << 1) + 1;
node = node->childred[1 - bit];
}
else
{
xorRes = (xorRes << 1);
node = node->childred[bit];
}
}
res = std::max(res, xorRes);
}
return res;
}
int findMaximumXOR(vector<int> &nums)
{
TrieNode *root = buildTrie(nums);
return getMaxXOR(nums, root);
}
2、总结:
- 求两个整数的异或值,可以使用xor运算符实现,通过两层遍历,让数组中的元素两两异或操作,求得最大值。
- 采用前缀树解法,对整数的单个位数创建树结构时,创建的是一棵二叉树,这个一定要区分开,这快理解不了的,可以通过画图辅助,从根节点到叶子结点的每条路径代表一个整数
- 那要求两个整数的异或结果值最大,一个是进位要相反其结果才为1,二是越高位其异或值的结果越大,所以构建二叉树时,是先从最高位开始构建结点。
- 在求取异或值时,也是从高位开始,查找与之相反的进位结果,也就是当前进位为0,那就要查找对应进位结点的值为1的结点,反之为0.
- 最后是注意位移和进位运算,要仔细。