剑指 Offer II 067. 最大的异或(421. 数组中两个数的最大异或值)

题目:

 

思路:

【1】暴力破解的方式

【2】哈希表的方式

【3】字典树的方式

代码展示:

字典树的方式:

//时间73 ms击败96.85%
//内存60.3 MB击败47.71%
//时间复杂度:O(nlog⁡C),其中 n 是数组 nums 的长度,C 是数组中的元素范围,在本题中 C<2^31。
//我们需要将 a[0] 到 a[n−2] 加入字典树中,并且需要以 a[1] 到 a[n−1]​ 作为「参照对象」在字典树上进行遍历,每一项操作的单次时间复杂度为 O(log⁡C),因此总时间复杂度为 O(nlog⁡C)。
//空间复杂度:O(nlog⁡C)。每一个元素在字典树中需要使用 O(log⁡C) 的空间,因此总空间复杂度为 O(nlog⁡C)。
class Solution {
    // 字典树的根节点
    Trie root = new Trie();
    // 最高位的二进制位编号为 30
    static final int HIGH_BIT = 30;

    public int findMaximumXOR(int[] nums) {
        int n = nums.length;
        int x = 0;
        for (int i = 1; i < n; ++i) {
            // 将 nums[i-1] 放入字典树,此时 nums[0 .. i-1] 都在字典树中
            add(nums[i - 1]);
            // 将 nums[i] 看作 ai,找出最大的 x 更新答案
            x = Math.max(x, check(nums[i]));
        }
        return x;
    }

    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;
            }
        }
    }

    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) {
                // a_i 的第 k 个二进制位为 0,应当往表示 1 的子节点 right 走
                if (cur.right != null) {
                    cur = cur.right;
                    x = x * 2 + 1;
                } else {
                    cur = cur.left;
                    x = x * 2;
                }
            } else {
                // a_i 的第 k 个二进制位为 1,应当往表示 0 的子节点 left 走
                if (cur.left != null) {
                    cur = cur.left;
                    x = x * 2 + 1;
                } else {
                    cur = cur.right;
                    x = x * 2;
                }
            }
        }
        return x;
    }
}

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

 

哈希表的方式:

//时间196 ms击败22.52%
//内存59 MB击败79.68%
//时间复杂度:O(nlog⁡C),其中 n 是数组 nums 的长度,C 是数组中的元素范围,在本题中 C<2^31。
//枚举答案 x 的每一个二进制位的时间复杂度为 O(log⁡C),在每一次枚举的过程中,我们需要 O(n) 的时间进行判断,因此总时间复杂度为 O(nlog⁡C)。
//空间复杂度:O(n),即为哈希表需要使用的空间。
class Solution {
    // 最高位的二进制位编号为 30
    static final int HIGH_BIT = 30;

    public int findMaximumXOR(int[] nums) {
        int x = 0;
        for (int k = HIGH_BIT; k >= 0; --k) {
            Set<Integer> seen = new HashSet<Integer>();
            // 将所有的 pre^k(a_j) 放入哈希表中
            for (int num : nums) {
                // 如果只想保留从最高位开始到第 k 个二进制位为止的部分
                // 只需将其右移 k 位
                seen.add(num >> k);
            }

            // 目前 x 包含从最高位开始到第 k+1 个二进制位为止的部分
            // 我们将 x 的第 k 个二进制位置为 1,即为 x = x*2+1
            int xNext = x * 2 + 1;
            boolean found = false;
            
            // 枚举 i
            for (int num : nums) {
                if (seen.contains(xNext ^ (num >> k))) {
                    found = true;
                    break;
                }
            }

            if (found) {
                x = xNext;
            } else {
                // 如果没有找到满足等式的 a_i 和 a_j,那么 x 的第 k 个二进制位只能为 0
                // 即为 x = x*2
                x = xNext - 1;
            }
        }
        return x;
    }
}

 

哈希表的优化:

//时间20 ms击败99.65%
//内存66.8 MB击败65.41%
class Solution {
    public int findMaximumXOR(int[] nums) {
        int max = 0;
        for (int num: nums) {
            max = Math.max(max, num);
        }
        int len = 32 - Integer.numberOfLeadingZeros(max);
        Set<Integer> prefixSet = new HashSet<>();
        int mask = 0;
        for (int i = len - 1; i >= 0; --i) {
            mask <<= 1;
            int prefix = mask | 1; // len最高位是1
            prefixSet.clear(); // 清除Set
            for (int num: nums) {
                int cur = num >> i; // 右移i位
                if (prefixSet.contains(cur ^ prefix)) { // 异或
                    mask |= 1;
                    break;
                }
                prefixSet.add(cur);
            }
        }
        return mask;
    }
}

 

暴力破解的方式:

//超出时间限制,(当输入的元素过多的时候会超时)
class Solution {
    public int findMaximumXOR(int[] nums) {
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < nums.length;i++){
            for (int j = i; j < nums.length;j++){
                max = Math.max(max,nums[i]^nums[j]);
            }
        }
        return max;
    }
}

 

posted @ 2023-03-28 12:24  忧愁的chafry  阅读(12)  评论(0编辑  收藏  举报