算法---LeetCode 421. 数组中两个数的最大异或值

1. 题目

原题链接

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

Related Topics 位运算 字典树 数组 哈希表
364 0

2. 题解

2.1 解法1: 暴力解法(超时)

    class Solution {
        public int findMaximumXOR(int[] nums) {
            int ans = 0;
            for (int i = 0; i < nums.length; i++) {
                for (int j = 0; j < nums.length; j++) {
                    ans = Math.max(ans, nums[i] ^ nums[j]);
                }
            }
            return ans;
        }
    }

2.2 解法2: 前缀树(字典树)

  1. 构建二进制前缀树
    具体来说就是利用数的二进制表示,从高位到低位构建一棵树(因为只有0和1 两个值,所以是一棵二叉树),每个从根节点到叶子节点的路径都表示一个数。(构建的树看下图)

  2. 搜索前缀树
    然后遍历数组中的数字,将每一个二进制位,在对应的层中找到一个异或的最大值,也就是:如果是1,找0的那条路径,如果是0,找1的那条路径。
    这样搜索下来的路径就是这个数字和整个数组异或的最大值,如下图

算法---LeetCode 421. 数组中两个数的最大异或值_第1张图片
代码:

    class Solution {
        // son[0] 代表 0, son[1] 代表 1
        class Node {
            Node[] son = new Node[2];
        }
        // 字典树的根节点
        Node root = new Node();

        public int findMaximumXOR(int[] nums) {
            int res = 0;
            for (int x : nums) insert(x);
            for (int x : nums) {
                res = Math.max(res, search(x));
            }
            return res;
        }

        void insert(int x) {
            Node p = root;
            // 最高位的二进制位编号为 30
            for (int i = 30; i >= 0; i--) {
                // 取第 i 位数字
                int t = (x >> i) & 1;
                if (p.son[t] == null) {
                    p.son[t] = new Node();
                }
                p = p.son[t];
            }
        }

        int search(int x) {
            Node p = root;
            int res = 0;
            for (int i = 30; i >= 0; i--) {
                int t = (x >> i) & 1;
                if (t == 0) {
                    // 第i位二进制位为0, 若要找最大应此时需要找 1 的结点
                    if (p.son[1] != null) {
                        p = p.son[1];
                        res = res * 2 + 1;
                    } else {
                        p = p.son[0];
                        res = res * 2;
                    }
                } else {
                    // 第i位二进制位为1, 若要找最大应此时需要找 0 的结点
                    if (p.son[0] != null) {
                        p = p.son[0];
                        res = res * 2 + 1;
                    } else {
                        p = p.son[1];
                        res = res * 2;
                    }
                }
            }
            return res;
        }

    }

参考:
Python3 巧妙利用前缀树剪枝,复杂度 O(N)
宫水三叶の相信科学系列】详解为何能用「贪心」&「Trie」找「最大异或结果」

写法2:

由于只有由于字典树中的每个节点最多只有两个子节点,分别表示 0 和 1,因此本题中的字典树是一棵二叉树。在设计字典树的数据结构时,我们可以令左子节点 left 表示 0,右子节点 right 表示 1

    class Solution {
        class Node {
            // 左子树指向表示 0 的子节点
            Node left;
            // 右子树指向表示 1 的子节点
            Node right;
        }

        // 字典树的根节点
        Node root = new Node();

        public int findMaximumXOR(int[] nums) {
            // 构建字典树
            for (int x : nums) {
                add(x);
            }
            int ans = 0;
            // 遍历查找最大值
            for (int x : nums) {
                ans = Math.max(ans, search(x));
            }
            return ans;
        }

        // 将一个数添加到字典树中
        public void add(int x) {
            Node cur = root;
            for (int i = 30; i >= 0; i--) {
                int bit = (x >> i) & 1;
                if (bit == 0) {
                    if (cur.left == null) {
                        cur.left = new Node();
                    }
                    // 已经存在结点, 则向下推进
                    cur = cur.left;
                } else {
                    if (cur.right == null) {
                        cur.right = new Node();
                    }
                    cur = cur.right;
                }
            }
        }

        // 查找与 x 异或的最大值
        public int search(int x) {
            int res = 0;
            Node cur = root;
            for (int i = 30; i >= 0; i--) {
                int bit = (x >> i) & 1;
                if (bit == 0) {
                    //  x 的第 i 个二进制位为 0,应当往表示 1 的子节点 right 走
                    if (cur.right != null) {
                        res = res * 2 + 1;
                        cur = cur.right;
                    } else {
                        res = res * 2;
                        cur = cur.left;
                    }

                } else {
                    //  x 的第 i 个二进制位为 1,应当往表示 0 的子节点 left 走
                    if (cur.left != null) {
                        res = res * 2 + 1;
                        cur = cur.left;
                    } else {
                        res = res * 2;
                        cur = cur.right;
                    }
                }
            }
            return res;
        }
    }

参考: 官方题解

你可能感兴趣的:(算法,数据结构,leetcode)