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

题目

给你一个整数数组 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

提示:

  • 1 <= nums.length <= 2 * 104
  • 0 <= nums[i] <= 231 - 1
思路

最简单的方法是暴力枚举所有可能的两个数,但时间复杂度为O(n2);
另一种方法是贪心,我们可以这样想:要想让异或的值越大,就要尽量让两个数对应的每一位都不同(异或的规则是:相同为零,不同为1)
我们可以先把num[j]之前的数全都用一颗前缀树存起来,不包括num[j]。
在枚举num[j]时判断其二进制下的每一位,如果当前位为0,就查找Trie树中是否有在对应位置为1的数,如果没有,只能走0路径;
如果当前位为1,就查找Trie树中是否有在对应位置为0的数,如果没有,只能继续走1路径;
这样一直走到最底层,就可以找到与num[j]异或最大的值num[i],并求出最后的结果;

Trie树

Trie树的核心操作是插入和查询,如果是用循环控制递归的深度,那么不用设置是否为叶节点的标志;如果是直接递归,则需要添加一个标记是否为叶节点的标志;

代码
class Solution {
    //字典树的根节点
    private Trie root = new Trie();

    //最大的二进制编号
    static final int HIGH_BIT = 30;

    public int findMaximumXOR(int[] nums) {
        int res = 0;
        //这里的循环并没有考虑nums[0]和nums[0]异或的情况,因为自身和自身异或一定为零
        for(int i = 1; i < nums.length; i ++) {
            //将该数字加入字典树
            insert(nums[i - 1]);
            //将nums[i]看做ai,找出最大的x更新答案
            res = Math.max(res, check(nums[i]));
        }
        return res;
    }

    public void insert(int num) {
        Trie cur = root;
        //所有元素都是等长的
        for(int k = HIGH_BIT; k >= 0; k --) {
            int bit = (num >> k) & 1; //判断第i位是1还是0
            if(bit == 0) {
                //如果当前节点不存在,则新建一个
                if(cur.left == null) cur.left = new Trie();
                cur = cur.left;
            }
            else {
                if(cur.right == null) cur.right = new Trie();
                cur = cur.right;
            }
        }
    }

    public int check(int num) {
        Trie cur = root;
        int ans = 0;
        for(int k = HIGH_BIT; k >= 0; k --) {
            int bit = (num >> k) & 1;
            //如果当前位是0, 则判断是否存在1的路径
            if(bit == 0) {
                if(cur.right != null) {
                    ans = ans * 2 + 1;
                    cur = cur.right;
                } else {
                    ans = ans * 2;
                    cur = cur.left;
                }
            }
            else {
                if(cur.left != null) {
                    ans = ans * 2 + 1;
                    cur = cur.left;
                } else {
                    ans = ans * 2;
                    cur = cur.right;
                }
            }
        }
        return ans;
    }
}

//这里每个数都是固定32位长的,所以不用添加结束标志
class Trie {
    Trie left = null;
    Trie right = null;
}

你可能感兴趣的:(#,LeetCode)