给你一个整数数组 nums ,返回 nums[i] XOR nums[j] 的最大运算结果,其中 0 ≤ i ≤ j < n 。
进阶:你可以在 O(n) 的时间解决这个问题吗?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-xor-of-two-numbers-in-an-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
一开始看到进阶,还以为可以用暴力法写完再优化呢,结果提交超时。
class Solution(object):
def findMaximumXOR(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
result = 0
for i in range(0, len(nums) - 1):
for j in range(i, len(nums)):
r = nums[i] ^ nums[j]
if r > result:
print("{},{}={}".format(nums[i], nums[j], r))
result = r
return result
题解
下面的内容来自:https://kingsfish.github.io/2017/12/15/Leetcode-421-Maximum-XOR-of-Two-Numbers-in-an-Array/ 这个文章很好,讲的很详细大体能明白。
我们还需要用上一个异或的特性,假设a和b产生了最终的答案max,即a ^ b = x,那么根据异或的特性,a ^ x = b。同理,a和b的最高位(前n位)也有相同的性质。
先以最高位为例子,我们可以把所有的数字的最高位放到一个HashSet里面,然后使用1与set里面的所有数字进行异或,如果得出的结果仍然在set里面,那么最终结果的最高位必然为1,否则为0。也即,先假定结果为1,然后与set中所有数字异或,假定a与1异或得到结果b(a ^ 1 = b),而b仍然在set里面,那么说明set中有两个数字异或能得到1(a ^ b = 1)。否则,set中没有两个数字能够异或得到1,那么最终结果的最高位为1的假设失败,说明最终结果的最高位为0。以此类推可以得到第二位、第三位。。。的数字。
再做一下推广,我们将所有数字的前N位放到一个HashSet里面,然后使用之前N-1位得到的最大值前缀prefix与set里面的所有数字进行异或,如果得出的结果仍然在set中,那么第N位必然为1,否则为0。
举个例子,给定数组[14, 11, 7, 2],二进制表示分别为[1110, 1011, 0111, 0010]。题目说了,数字最长不会超过32位,所以应从i = 31开始,但是所有数字中最多位4位数,简单起见,我直接从最高位i=3开始
我理解的是:根据二进制,从高位开始建树,0就往左孩子走,1就往右孩子走。
建好树之后,从高位开始查询数字a
- 如果当前位是1,而且当前节点有右孩子,就往右孩子走;如果没有右孩子之只能往左孩子走
- 如果当前位是0,而且当前节点有左孩子,就往左孩子走;如果没有左孩子之只能往右孩子走
- 如果当前节点既没有左孩子也没有右孩子,就说明当前节点到了最下面一层,查询到当前节点b的数字,与a最大异或值的数就是b
遍历输入数据,用result记录最大的异或结果即可。
建树
py不熟悉指针,想换成c++。结果太久没有使用指针,出了好多错误。
大概思路是把2 31 -1里的数建成一个二叉树的表。
用2 3 -1 = 7,[0,7]范围内的数,表示成二进制,举个例子:
从高位开始建树,由于5 对应的二进制是[1,0,1],6 对应的二进制是[1,1,0]
根据左孩子是0,右孩子是1的原则,开始建树
[0,7]范围内的数,全部表示一下
由于先要用到高位是0还是1,需要用到位运算
例如要从高到低取6,各位上的数
- 取第3位
先将6(110)右移2位,变成1,再和数字1进行与运算,最后得到1,就是6第3位的数
右移两位相当于原来的数除22 - 取第2位
先将6(110)右移1位,变成11,再和数字1进行与运算,最后得到1,就是6第2位的数
右移两位相当于原来的数除21 - 取第1位
先将6(110)右移0位,变成110,再和数字1进行与运算,最后得到0,就是6第1位的数
右移两位相当于原来的数除20,也就是不变。
当nums = [2,5,6]的时候,建好的树为:
查询
根据数的从高到低位依次查询相反数,这样才能使获得的异或数最大。
当nums = [2,5,6]的时候,建好树之后,增加一个和根节点一样的当前节点
根据顺序查询,首先是2,
- 取第3位是0,查询当前节点有没有 0的相反数也就是1 分支,发现有 ,将当前节点向右孩子移动
- 取第2位是1,查询当前节点有没有 1的相反数也就是0 分支,发现有 ,将当前节点向左孩子移动
- 取第1位是0,查询当前节点有没有 0的相反数也就是1 分支,发现有 ,将当前节点向右孩子移动
与2最大异或值的数就是5,异或结果为7
其次查询5,与5最大异或值的数就是3,异或结果为7
其次查询6,与6最大异或值的数就是2,异或结果为5
最终,当nums = [2,5,6]的时候,最大的异或结果为7
ac代码
c++
#include <bits/stdc++.h>
using namespace std;
struct Tire
{
Tire *left = nullptr;
Tire *right = nullptr;
int value = 0;
Tire() {
}
};
class Solution
{
public:
Tire * root = new Tire();
int add(int num)
{
//cout<<"add"<<num<<endl;
Tire *cur = root;
for(int j = 31-1; j>=0; j--)
{
if(1==((num>>j)&1))
{
if (!cur->right)
{
cur->right = new Tire();
}
//cout<<"right,";
cur->value = num;
cur= cur->right;
}
else
{
if (!cur->left)
{
cur->left = new Tire();
}
//cout<<"left,";
cur->value = num;
cur = cur->left;
}
if(j==0)
{
cur->value = num;
//cout<<num<<endl;
}
}
return 0;
}
int findMaximumXOR(vector<int>& nums)
{
for(int i :nums)
{
add(i);
}
int result = 0;
for(int i:nums)
{
//cout<<"look"<<i<<endl;
Tire *cur = root;
for(int j = 31-1; j>=0; j--)
{
if(0==((i>>j)&1))
{
if(cur->right)
{
//cout<<"right,";
cur = cur->right;
}
else
{
//cout<<"left,";
cur = cur->left;
}
}
else
{
if(cur->left)
{
//cout<<"left,";
cur = cur->left;
}
else
{
//cout<<"right,";
cur = cur->right;
}
}
if(j==0)
{
//cout<<cur->value<<endl;
result = max(result,i^cur->value);
}
}
}
return result;
}
};
int main()
{
int a[] = {
8,10,2};
vector<int> nums ;
for (int i :a)
{
nums.push_back(i);
}
Solution s ;
cout << s.findMaximumXOR(nums) << endl;
return 0;
}