【leetcode】421. 数组中两个数的最大异或值

给你一个整数数组 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. 如果当前位是1,而且当前节点有右孩子,就往右孩子走;如果没有右孩子之只能往左孩子走
  2. 如果当前位是0,而且当前节点有左孩子,就往左孩子走;如果没有左孩子之只能往右孩子走
  3. 如果当前节点既没有左孩子也没有右孩子,就说明当前节点到了最下面一层,查询到当前节点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,各位上的数

  1. 取第3位
    先将6(110)右移2位,变成1,再和数字1进行与运算,最后得到1,就是6第3位的数
    右移两位相当于原来的数除22
  2. 取第2位
    先将6(110)右移1位,变成11,再和数字1进行与运算,最后得到1,就是6第2位的数
    右移两位相当于原来的数除21
  3. 取第1位
    先将6(110)右移0位,变成110,再和数字1进行与运算,最后得到0,就是6第1位的数
    右移两位相当于原来的数除20,也就是不变。

当nums = [2,5,6]的时候,建好的树为:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210516200632629.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3MjUyNTE5,size_16,color_FFFFFF,t_70

查询

根据数的从高到低位依次查询相反数,这样才能使获得的异或数最大。

当nums = [2,5,6]的时候,建好树之后,增加一个和根节点一样的当前节点
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210516200822924.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3MjUyNTE5,size_16,color_FFFFFF,t_70
根据顺序查询,首先是2,

  1. 取第3位是0,查询当前节点有没有 0的相反数也就是1 分支,发现有 ,将当前节点向右孩子移动
    在这里插入图片描述
  2. 取第2位是1,查询当前节点有没有 1的相反数也就是0 分支,发现有 ,将当前节点向左孩子移动
    在这里插入图片描述
  3. 取第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;
}

Guess you like

Origin blog.csdn.net/qq_37252519/article/details/116901620