LeetCode421. 数组中两个数的最大异或值

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

2021.5.16 每日一题

题目描述
给你一个整数数组 nums ,返回 nums[i] XOR nums[j] 的最大运算结果,其中 0 ≤ i ≤ j < n 。

进阶:你可以在 O(n) 的时间解决这个问题吗?

示例 1:

输入: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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-xor-of-two-numbers-in-an-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

挺难的
看到题以后想到如何尽可能让每一位都为1,而且是越高位越好,这样结果就能最大,然后就想怎么确定每一位为1,只能想到让数组中高位为1的大数与其他异或找最大的,达不到O(n)的复杂度
然后去看题解:

哈希表

第一个方法,一个数字有31个二进制位,从高到低确定每一位是否能为1
那么如何确定每一位是否能为1呢,31一个二进制位逐一判断
先将每一个数移动k位放入哈希表中,然后对于之前得到的最大数 x,判断能否与数组中的数异或,得到使当前第k + 1位为1的结果

class Solution {
    public int findMaximumXOR(int[] nums) {
        //中午想了半天也不会做啊,感觉就是怎样想办法让每一位都为1,尽可能高位为1,
        //用包含最高位的大数和其他数相异或吧,然后找最大的,就是我的思路了
        //然后看了标签,字典树?还是不懂

        //第一个题解,一个数字有31个二进制位,从高到低确定每一位是否能为1
        //那么如何确定每一位是否能为1呢,31一个二进制位逐一判断
        //先将每一个数移动k位放入哈希表中,然后对于之前得到的最大数,判断能否与数组中的数异或,得到使当前第k位为1的结果

        //这样想我直观想到的一个问题就是最终结果是两个数异或的结果吗
        //然后就试着举了个栗子,因为是从高到低,第一位肯定是
        int x = 0;
        //30位依次遍历
        for(int i = 30; i >= 0; i--){
            Set<Integer> set = new HashSet<>();

            //先把每一个数移动i位的结果放入哈希表中
            for(int num : nums){
                set.add(num >> i);
            }
            //使当前位为1
            int nextX = x * 2 + 1;
            boolean found = false;
            //a ^ b = x ==>> a = b ^ x;
            for(int num : nums){
                if(set.contains(nextX ^ (num >> i))){
                    found = true;
                    break;
                }
            }

            if(found){
                x = nextX;
            }else{
                x *= 2; 
            }
        }
        return x;
    }
}

这样想我直观想到的一个问题就是最终结果是两个数异或的结果吗
然后就试着举了个栗子,因为是从高到低,最高位肯定只有两种情况,0或者1,哈希表中只有两个数,肯定能取1,此时xNext 为1 ,x最终结果为1,此时两个数最高位肯定是0 和 1
第二位可能有四种情况,00,01,10,11, 哈希表中最多有四个数,xNext = 11,而x的最终结果是10 或者11,如果最终结果是11,即此时两个相异或的数,前两位肯定是00和11 或者01和10
再取第三位,同理,8种情况,哈希表中最多8个数,xNext = 111,而x的最终结果是110或者111,如果最终结果这次是110的话,前三位就是在前两位的基础上更进一步,取的数只能是更加精确的两个数
这样处理到最后,就可以得到完整的两个数

字典树

根据上面的哈希表思路想字典树,就好想很多了
从高位到低位创建字典树,每一个结点两个分叉,即是一颗二叉树
在构建后字典树之后,对于数组中的每一个数,都在字典树中找能使得异或结果最大的数
然后再取所有最大数的最大值
具体实现过程中,可以在构建字典树的同时,找当前数与前面数异或的最大值。这样也能保证不漏掉结果

class Trie{
    //每个节点两个子节点
    Trie[] node = new Trie[2];
}
class Solution {
    //根节点
    Trie root = new Trie();
    public int findMaximumXOR(int[] nums) {
        //字典树,试着回想一下写一下
        int l = nums.length;
        int x = 0;
        //i 时,添加i - 1,并计算nums[i]的异或最大值
        for(int i = 1; i < l; i++){
            add(nums[i - 1]);
            x = Math.max(x, check(nums[i]));
        }
        return x;
    }

    public void add(int num){
        //应该有31层
        Trie temp = root;
        for(int k = 30; k >= 0; k--){
            //取当前位
            int cur = (num >> k) & 1;
            //如果存在节点,就往下走,不存在就创建
            if(temp.node[cur] != null){
                temp = temp.node[cur]; 
            }else{
                temp.node[cur] = new Trie();
                temp = temp.node[cur];
            }
        }
    }


    public int check(int num){
        Trie temp = root;
        int x = 0;
        for(int k = 30; k >= 0; k--){
            int cur = (num >> k) & 1;
            int find = 1 - cur;     //cur为1,找0,cur为0找1
            if(temp.node[find] != null){
                //如果有find,就说明异或结果为1
                x = x * 2 + 1;
                temp = temp.node[find];
            }else{
                //否则为0
                x = x * 2;
                temp = temp.node[cur];
            }
        }
        return x;
    }
}

没想到写完,一提交就过了,看来字典树也掌握的还行哈哈

你可能感兴趣的:(LeetCode,leetcode,算法,java)