题目:
Given a non-empty array of numbers,
a0, a1, a2, … , an-1
, where 0 ≤ ai < 231.
Find the maximum result ofai
XORaj
, where0 ≤ i, j < n
.
Could you do this in O(n) runtime?
Example:
Input: [3, 10, 5, 25, 2, 8]
Output: 28
Explanation: The maximum result is 5 ^ 25 = 28.
解释:
这题还能用字典树做?
异惑运算的自反性a^b = c
->a^c=b
,b^c=a
位操作,最多有32位
bin(1<<31) 表示把1转为2进制后左移31位
‘0b10000000000000000000000000000000’ (31个0)
题目中给定了数字的返回不会超过231,那么最多只能有32位,我们用一个从左往右的mask
,用来提取数字的前缀,然后将其都存入set
中,我们用一个变量temp
,用来验证当前位为1的数字再或上之前结果res(相当与把res后面的一位置1),看结果temp
和set
中的前缀异或之后在不在set
中,这里用到了一个性质,若a^b=c
,那么a=b^c
,因为temp
是我们要验证的当前最大值,所以我们遍历set
中的数时,若和temp
异或后的结果仍在set
中,说明两个前缀可以异或出temp
的值,也就是当前temp的值可以通过nums
中的两个num
异或再取前缀得到,所以我们更新res
为temp
,继续遍历求解maxres。
从最高位往最低位求解maxres,但是这种解法不仅不好理解,时间复杂度也不是O(n).
python代码:
class Solution(object):
def findMaximumXOR(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
mask=0
maxres=0
for i in xrange(31,-1,-1):
#The mask will grow like 100..000 , 110..000, 111..000, then 1111...111
mask|=1<<i
_set=set()
for num in nums:
_set.add(mask&num)
temp=maxres|(1<<i)
for s in _set:
if temp^s in _set:
maxres=temp
break
return maxres
c++代码 (超时):
class Solution {
public:
int findMaximumXOR(vector<int>& nums) {
int mask=0;
int maxRes=0;
for (int i=31;i>=0;i--)
{
mask= mask | (1<<i);
set<int> _set;
for (auto num:nums)
_set.insert(mask&num);
int temp=maxRes| (1<<i);
for (auto s :_set)
{
if (find(_set.begin(),_set.end(),temp^s)!=_set.end())
{
maxRes=temp;
break;
}
}
}
return maxRes;
}
};
前缀树解法:
前缀树+分治法
根据题目描述,我们需要找到最大的异或值,异或代表了两个数的二进制的不同程度,且越高位越不一样,异或值就越大,由于是按位比较,所以使用Trie树来当做基础数据结构。
我们可以总结出以下几点:
1.因为整型的位数是固定的,排除第一位符号位,Trie树的高度是固定的,即32层(包括root)
2.由于只有0和1两个子节点,所以为了节省空间,可以使用二叉树的方式(或者数组和HashMap均可)
3.由于是异或,前缀位如果相同,异或值都是0,所以可以先找到第一个两个子节点都不为空的节点当做root
分治法,原则就是尽量使两个分支的高位不同
* one是1分支,zero是0分支,可以看做图中的左区和右区
* 1. 当1分支的下一位只有1时,找0分支的0,若没有,就找0分支的1
* 2. 当1分支的下一位只有0时,找0分支的1,若没有,就找0分支的0
* 3. 当1分支的下一位0,1均有时,看0分支:如果0分支只有1,则1分支走0,反之则走1
* 4. 当0,1两个分支均有两个下一位是,尝试1分支走1,0分支走0和1分支走0,0分支走1并取最大值
disscuss区的人用java实现这样的是算法速度超快,为什么我用python实现的速度超慢?可能是有的测试用例并没有31位数字那么大,但是无论如何我们构造Trie的时候都是构造一个32层的Trie,所以速度慢了。但是慢也无所谓,主要是学习了Trie的思想:
class TrieNode(object):
def __init__(self):
self.zero=None
self.one=None
self.val=None
self.isEnd=False
class Solution(object):
def findMaximumXOR(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
#把num插入到Trie中,按照从高位往低位的顺序插入
def insert(root, num):
current=root
mask=1<<30;
for i in range(31):
bitNum= 0 if mask&num==0 else 1
if bitNum==0 and not current.zero:
current.zero=TrieNode()
if bitNum==1 and not current.one:
current.one=TrieNode()
current=current.zero if bitNum==0 else current.one
mask>>=1
#当前的current指向最低位
current.isEnd=True
current.val=num
#原则就是使两个分支的高位尽量不同
'''
分治法,原则就是尽量使两个分支的高位不同
one是1分支,zero是0分支,可以看做图中的左区和右区
1. 当1分支的下一位只有1时,找0分支的0,若没有,就找0分支的1
2. 当1分支的下一位只有0时,找0分支的1,若没有,就找0分支的0
3. 当1分支的下一位0,1均有时,看0分支:如果0分支只有1,则1分支走0,反之则走1
4. 当0,1两个分支均有两个下一位是,尝试1分支走1,0分支走0和1分支走0,0分支走1并取最大值
'''
def maxHelper(one,zero):
if one.isEnd and zero.isEnd:
return one.val^zero.val
if not one.zero:
return maxHelper(one.one,zero.zero if zero.zero else zero.one )
elif not one.one:
return maxHelper(one.zero,zero.one if zero.one else zero.zero )
elif not zero.zero:
return maxHelper(one.zero, zero.one)
elif not zero.one:
return maxHelper(one.one, zero.zero)
else:
return max(maxHelper(one.one, zero.zero),maxHelper(one.zero, zero.one))
if len(nums)<=1:
return 0
root =TrieNode()
for num in nums:
insert(root,num)
cur=root
while not cur.zero or not cur.one:
cur=cur.zero if cur.zero else cur.one
#cur现在指向同时拥有01子结点的结点
return maxHelper(cur.one,cur.zero)
c++解法的速度简直快:
struct TrieNode {
bool isEnd;
int val;
TrieNode* zero;
TrieNode* one;
TrieNode() : isEnd(false),zero(NULL),one(NULL){}
};
class Solution {
public:
int findMaximumXOR(vector<int>& nums) {
TrieNode* root=new TrieNode();
if (nums.size()<=1)
return 0;
for (auto num:nums)
insert(root,num);
TrieNode* cur=root;
while(cur->zero==NULL || cur->one==NULL)
{
//cout<<cur->val<<endl;
cur= (cur->zero)?(cur->zero):(cur->one);
}
return maxHelper(cur->one,cur->zero);
}
void insert(TrieNode* root ,int num)
{
TrieNode* current=root;
int mask=1<<30;
for (int i=0;i<31;i++)
{
int bitNum= (int(mask&num)==0)?0:1;
if (bitNum==0 && current->zero==NULL)
current->zero=new TrieNode();
if(bitNum==1 && current->one==NULL)
current->one=new TrieNode();
current= (bitNum==0)?current->zero:current->one;
mask>>=1;
}
current->isEnd=true;
//cout<<num<<endl;
current->val=num;
}
//分治
int maxHelper(TrieNode* one,TrieNode* zero)
{
if (one->isEnd && zero->isEnd)
return one->val^zero->val;
if (!one->zero)
return maxHelper(one->one,zero->zero?zero->zero:zero->one );
else if (!one->one)
return maxHelper(one->zero,zero->one? zero->one :zero->zero );
else if (!zero->zero)
return maxHelper(one->zero, zero->one);
else if (!zero->one)
return maxHelper(one->one, zero->zero);
//两个都有
else
return max(maxHelper(one->one, zero->zero),maxHelper(one->zero, zero->one));
}
};
总结: