剑指 Offer II 067. 最大的异或(421. 数组中两个数的最大异或值)
题目:
思路:
【1】暴力破解的方式
【2】哈希表的方式
【3】字典树的方式
代码展示:
字典树的方式:
//时间73 ms击败96.85% //内存60.3 MB击败47.71% //时间复杂度:O(nlogC),其中 n 是数组 nums 的长度,C 是数组中的元素范围,在本题中 C<2^31。 //我们需要将 a[0] 到 a[n−2] 加入字典树中,并且需要以 a[1] 到 a[n−1] 作为「参照对象」在字典树上进行遍历,每一项操作的单次时间复杂度为 O(logC),因此总时间复杂度为 O(nlogC)。 //空间复杂度:O(nlogC)。每一个元素在字典树中需要使用 O(logC) 的空间,因此总空间复杂度为 O(nlogC)。 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(nlogC),其中 n 是数组 nums 的长度,C 是数组中的元素范围,在本题中 C<2^31。 //枚举答案 x 的每一个二进制位的时间复杂度为 O(logC),在每一次枚举的过程中,我们需要 O(n) 的时间进行判断,因此总时间复杂度为 O(nlogC)。 //空间复杂度: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; } }