421. Maximum XOR of Two Numbers in an Array(python+cpp)(包含前缀树解法)

题目:

Given a non-empty array of numbers, a0, a1, a2, … , an-1, where 0 ≤ ai < 231.
Find the maximum result of ai XOR aj, where 0 ≤ 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),看结果tempset中的前缀异或之后在不在set中,这里用到了一个性质,若a^b=c,那么a=b^c,因为temp是我们要验证的当前最大值,所以我们遍历set中的数时,若和temp异或后的结果仍在set中,说明两个前缀可以异或出temp的值,也就是当前temp的值可以通过nums中的两个num异或再取前缀得到,所以我们更新restemp,继续遍历求解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));
    }
};

总结:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值