问题描述 链接到标题

421. 数组中两个数的最大异或值 (Medium) 给你一个整数数组 nums ,返回 nums[i] XOR nums[j] 的最大 运算结果,其中 0 ≤ i ≤ j < n

示例 1:

输入:nums = [3,10,5,25,2,8]
输出:28
解释:最大运算结果是 5 XOR 25 = 28.

示例 2:

输入:nums = [14,70,53,83,49,91,36,80,92,51,66,70]
输出:127

提示:

  • 1 <= nums.length <= 2 * 10⁵
  • 0 <= nums[i] <= 2³¹ - 1

解题思路 链接到标题

如果直接暴力,求解最大异或值,时间复杂度为 $O(n^2)$,必定会超时。

实际上,我们可以把每个数字的二进制表示看成字符串,因此可以使用 字典树 这一数据结构来优化求解异或值的过程。暴力情况下,求解 nums[i] 与其他值的异或值,需要遍历整个数组,时间复杂度为 $O(n)$,而利用字典树,我们可以将这个比较的时间复杂度从 $O(n)$ 优化为 $O(\log_2C)$,其中 $C$ 为数字的大小,$\log_2C$ 即为数字的二进制表示的位数。

如果我们要找到最大异或值,应该从数字的最高位开始比较,字典树也应该从数字的高位开始构建,由于 0 <= nums[i] <= 2^31 - 1,因此我们可以将 nums[i] 右移 j 位的结果插入字典树,j 从 $31$ 递减到 $0$。

代码 链接到标题

class Trie {
  public:
    Trie() :
        tree_{nullptr, nullptr}, end_(false) {
    }
    void Insert(int num) {
        Trie *node = this;
        for (int i = 31; i >= 0; --i) {
            int idx = ((num >> i) & 1);
            if (node->tree_[idx] == nullptr) {
                node->tree_[idx] = new Trie();
            }
            node = node->tree_[idx];
        }
        node->end_ = true;
    }
    int Count(int x) {
        Trie *node = this;
        int res = 0;
        for (int i = 31; i >= 0; --i) {
            int a = (x >> i) & 1, b = 1 - a;
            if (node->tree_[b] != nullptr) {
                res |= (1 << i);
                node = node->tree_[b];
            } else {
                node = node->tree_[a];
            }
        }
        return res;
    }

  private:
    vector<Trie *> tree_;
    bool end_;
};
class Solution {
  public:
    int findMaximumXOR(vector<int> &nums) {
        int res = 0;
        Trie *tree = new Trie();
        for (auto num : nums) {
            tree->Insert(num);
        }
        for (auto num : nums) {
            res = max(res, tree->Count(num));
        }
        return res;
    }
};