一刷250-剑指 Offer II 067. 最大的异或(同:421. 数组中两个数的最大异或值)

题目:
给定一个整数数组 nums ,返回 nums[i] XOR nums[j] 的最大运算结果,其中 0 ≤ i ≤ j < n 。
------------------------
示例:
输入:nums = [3,10,5,25,2,8]
输出:28
解释:最大运算结果是 5 XOR 25 = 28.
示例 2:

输入:nums = [0]
输出:0
示例 3:

输入:nums = [2,4]
输出:6
示例 4:

输入:nums = [8,10,2]
输出:10
示例 5:

输入:nums = [14,70,53,83,49,91,36,80,92,51,66,70]
输出:127
 
提示:
1 <= nums.length <= 2 * 104
0 <= nums[i] <= 231 - 1
进阶:你可以在 O(n) 的时间解决这个问题吗?
-------------------------
思路:
在一个数组中求两个数的异或最大值,需要考虑一下几点。

大的数异或后带来的增益较大,所以优先考虑大的数进行异或,也就是先考虑高位。
只有当两个二进制位不同,二进制位才会为1,所以,我们尽量找到两个很多位都不同的数进行异或。

现在来看看解题思路:
1、构建前缀树:将所有的数都插入前缀树中。与普通前缀树不同,这里是根据一个数的二进制位来插入,
普通前缀树是根据单词中的字符来插入。还有一点需要注意的是,我们优先考虑高位,
所以是从高位到低位进行插入。

// 定义前缀树
class TrieNode{
    TrieNode[] next;
    public TrieNode(){
        next = new TrieNode[2]; // 代表 0 1 两个二进制位
    }
}
---------------------
2、对于 nums 中的每个数 num ,都到前缀树中去搜索num最大异或的那个数,
然后计算最大异或值,最后,从这些异或值中挑出最大的一个就是要的答案。

重点:搜索的方法
异或值最大,我们就要尽量让每个异或位都和 num 对应的二进制位不同。

如果 num 当前位为 0,就到 next[1] 去搜索;
如果 num 当前位为 1,就到 next[0] 去搜索;
如果与 num 当前位相反的那一位为空,那就只能到相同的那一位去搜索了。
--------------------
class Solution {
    class TrieNode {//定义前缀树 类
        TrieNode[] next;
        public TrieNode(){
            next = new TrieNode[2];
        }
    }
    private TrieNode root;//前缀树根节点
    public void buildTrie(int[] nums) {
        root = new TrieNode();//初始化前缀树的根节点
        for (int num : nums) {//将所有数都插入 前缀树
            TrieNode cur = root;
            for (int i = 30; i >= 0; i--) {//这里也可以是31,但是nums中的数都是整数 !!!
                int d = num >> i & 1;//第32位都是标记位 都为0, 异或后的结果也是0
                if (cur.next[d] == null) {//所以不考虑这一位
                    cur.next[d] = new TrieNode();
                }
                cur = cur.next[d]; //!!
            }
        }
    }
    public int searchMaxXOR(int num) {
        TrieNode cur = root;//初始化 前缀树根节点
        int xorNum = 0;//定义变量表示 :与 num 异或值最大的数
        for (int i = 30; i >= 0; i--) {
            int d = num >> i & 1;//d为num当前的二进制位
            int theOther = (d - 1) * -1;//获取与d相反的二进制位 d为0 theother 为1 反之亦然
            if (cur.next[theOther] == null) {//若相反的一位为空,则只好走相同的一位
                cur = cur.next[d];
                xorNum = xorNum * 2 + d;//记录走的路径,走的路径就是最后与num异或最大的数
            }else {
                cur = cur.next[theOther];
                xorNum = xorNum * 2 + theOther;
            }
        }
        return num ^ xorNum;//返回num 异或的最大值
    }
    public int findMaximumXOR(int[] nums) {
        int maxXOR = Integer.MIN_VALUE;//结果集: 记录异或最大值 !!!
        buildTrie(nums);//构建前缀树: 将所有数都插入前缀树中
        for (int num : nums) {//查询每个数异或的最大值
            maxXOR = Math.max(maxXOR, searchMaxXOR(num));
        }
        return maxXOR;//返回最终异或的最大值
    }
}

LC

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值