Maximum XOR of Two Numbers in an Array 异或最大值

一、概述

给你一个数组,求出其中任意两个元素异或结果的最大值。

二、分析

O(n^2)的算法不用想,对于每一个元素都和其余所有元素相异或,维护最大值就行。
O(n)的比较难想。我没想出来,看了半天解释。
核心就是,异或这种运算,和乘法有一点点像,知道结果,知道其中一个因数,就能确定另外一个因数(这不是废话么,四则运算都行)
那么我们这个结果就通过贪心来构造。下面具体说明:
我的愿望是得到32个1,这样是最大的,所以首先,我第32位想要是1
第32位是1的前提条件是nums中存在这样两个元素,它们的第32位分别是1和0
那么这两个元素异或,就可以得到第32位为1的结果,剩下的位先不看
那么如何得到nums中所有元素的第32位呢?
取一个mask,第32位为1,剩下31位全是0,然后和nums中的所有元素相与,就得到了所有元素的第32位
那接下来很简单了,如果第32位中既有1又有0,那么就证明结果的第32位为1
全扫一遍呗,看见1设置一个flag,看见0再设置一个flag,多简单
是啊,那我问你,要是想得到11,那么前提条件就是11和00,10和01,有四个。你得设置四个flag,想得到111呢?更多flag。
flag设置不过来了。
考虑异或的这样一个性质:a ^ b = c,则b ^ c = a
那么,我的结果想要得到第32位为1,那么让这个结果和所有元素的第32位相异或,得到的结果仍然在所有元素的第32位这个集合中,那么这个结果就是可以得到的
只用一位看起来不容易理解
比如说,我想要11100这个结果,那么,第5~3位为111和000,101和010,001和110等等都可以实现
我们通过mask可以得到所有元素的第5~3位,之后呢?
可以这样,看所有元素的第5~3位这样的数组的第一个元素,它是101,让它和其余所有元素相异或,得到的结果如果是111,那么说明11100可以实现
这种方法需要O(n^2)的时间复杂度
那按照异或的性质,我让111和第一个元素101异或,结果是010,要是010在所有元素的第5~3位中,那不就也是一样的效果
用set可以实现,时间复杂度降为O(n)
这个是贪心和异或性质的综合运用
贪心体现在哪呢?最开始的结果是0,第一次我期待第32位是1,要是成了,结果的第32位置1,要是不成,结果还是0
第一次不成,说明结果的第32位必为0,下一次期待的就是结果的第31位,贪心就体现在“期待的结果”是真正的结果中已确定的那一位后面的那一位置为1
例如,确定第32位为0,则下一个期待的结果就是第31位为1,01000…0,确定是1001 0000…000,下一个就是1001 1000…000。
另:引用自该网址,该题还可以用前缀树来做。原理是对于一个二进制的数,和它异或得到最大结果的是它按位取反,比如101按位取反得到010,101异或010就是111是最大的。
那么我们构造一棵前缀树,从根节点到叶子节点的每条路径都是一个nums中的元素。然后对nums中的每个元素按位取反,得到的结果分别从根节点往下走,能走多远走多远,匹配的最深的元素就是我们要的元素。
前缀树就是Tries。

三、总结

即使知道了异或的这个性质,想要做出这题也是有点难度。要知道利用mask将前面n位置为1,然后和每个元素相与,提取出前n位这个技巧,然后还要懂得贪心,来构造期待的最大结果。最后才是应用异或的性质。
位运算常用与操作进行提取,或操作置某位为1,这都是很常用的。
PS:位运算代码如下

class Solution {
public:
    int findMaximumXOR(vector<int>& nums) {
        int res=0;
        int mask=0;
        for(int i=31;i>=0;i--)
        {
            mask=mask|1<<i;
            unordered_set<int> s;
            for(auto num:nums)
                s.insert(num&mask);
            int except=res|1<<i;
            for(auto num:s)
                if(s.find(except^num)!=s.end())
                {
                    res=except;
                    break;
                }
        }
        return res;
    }
};

前缀树代码来自该网址

class Trie{
public:
	Trie* children[2];
public:
	Trie()
	{
		children[0] = nullptr;
		children[1] = nullptr;
	}
};
 
class Solution {
public:
	int findMaximumXOR(vector<int>& nums) {
		if (nums.empty()) return 0;
		Trie *root = new Trie();
		for (auto num : nums)
		{
			Trie * curNode = root;
			for (int i = 31; i >= 0; i--)
			{
				int curBit = (num >> i) & 1;
				if (curNode->children[curBit] == nullptr)
					curNode->children[curBit] = new Trie();
				curNode = curNode->children[curBit];
			}
		}
 
		int maxi = INT_MIN;
		for (auto num : nums)
		{
			Trie* curNode = root;
			int curSum = 0;
			for (int i = 31; i >= 0; i--)
			{
				int curBit = (num >> i) & 1;
				if (curNode->children[curBit ^ 1] != nullptr)
				{
					curSum += (1 << i);
					curNode = curNode->children[curBit ^ 1];
				}
				else curNode = curNode->children[curBit];
			}
			maxi = max(maxi, curSum);
		}
		return maxi;
	}
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值