421. 数组中两个数的最大异或值 | 字典树

37 阅读2分钟

题目描述:

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

本题有哈希表和字典树两种解法,这里着重记录一下字典树解法。

字典树在处理类似的题目上有着广泛的应用,基本思想是将一个数 num 的二进制表示存在树中。由于一个整型变量由 32 bit 表示,因此可以用高度为 32 的字典树进行存储。

下图以 4 bit 存储数字 11 为例,构建了一棵字典树。可以看到构建方法是从最高位开始,遇到 0 往左子树走,遇到 1 则往右子树走。

image.png

在代码中,当遍历到 nums[i] 时,先将 nums[0 ~ i-1] 都加入到字典树中,这样就能快速判断出 nums[0 ~ i-1] 中任意数与 nums[i] 异或能得到的最大值;当遍历到 nums[n-1] 时,就得到了数组中所有数两两异或的最大值了。

判断函数 check 的原理:以最高位为例,假设当前数 x 的最高位是 1,如果字典树 root 的左子树不为空,说明树中至少存在一个数 y 的最高位是 0,那么 x^y 的最高位必然是 1,由此可知 x 与树中存储的值的最大异或值的最高位一定可以为 1。其他位同理。

class Solution {
    Trie root = new Trie();
    static final int HIGH_BIT = 31;

    // 将数字 num 加入到字典树中
    public void add(int num) {
        Trie cur = root;
        for(int k = HIGH_BIT; k >= 0; k--) {
            int bit = (num >> k) & 1;
            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;
            }
        }
    }

    // 在当前的字典树条件下,num 能够异或得到的最大值
    public int check(int num) {
        Trie cur = root;
        int x = 0;
        for(int k = HIGH_BIT; k >= 0; k--) {
            int bit = (num >> k) & 1;
            if(bit == 0) {
                if(cur.right != null) {
                    cur = cur.right;
                    x |= (1 << k);
                } else {
                    cur = cur.left;
                }
            } else {
                if(cur.left != null) {
                    cur = cur.left;
                    x |= (1 << k);
                } else {
                    cur = cur.right;
                }     
            }
        }
        return x;
    }

    public int findMaximumXOR(int[] nums) {
        int ans = 0;
        // 不断往字典树中加入值,并判断当前的最大异或值
        for(int i = 1; i < nums.length; i++) {
            add(nums[i - 1]);
            ans = Math.max(ans, check(nums[i]));
        }
        return ans;
    }
}

class Trie {
    Trie left = null;
    Trie right = null;
}