[LeetCode] 421. Maximum XOR of Two Numbers in an Array
Given an integer array nums, return the maximum result of nums[i] XOR nums[j], where 0 <= i <= j < n.
Example 1:
Input: nums = [3,10,5,25,2,8]
Output: 28
Explanation: The maximum result is 5 XOR 25 = 28.
Example 2:
Input: nums = [14,70,53,83,49,91,36,80,92,51,66,70]
Output: 127
Constraints:
1 <= nums.length <= 2 * 105
0 <= nums[i] <= 231 - 1
数组中两个数的最大异或值。
给你一个整数数组 nums ,返回 nums[i] XOR nums[j] 的最大运算结果,其中 0 ≤ i ≤ j < n 。
思路
我这里提供一个位运算的做法,大部分内容参考了这个帖子。首先复习一下异或运算规则:两个二进制的数字,同一个位置上的数字不同,XOR 为 1;数字相同的时候,XOR 为 0。所以这里如果要找 XOR 最大的结果,最好能找到两个数字,他们的二进制表达最好在每一位上都不同。而且 XOR 是具有交换律的,如果你有 a ^ b = c,你同时可以得到 a ^ c = b 和 b ^ c = a。假如 a 和 b 是来自于 nums 的数字的话,我们为了要使 c 最大,我们可以试图去找一个前缀很大的数字 a 和一个前缀很小的数字 b,这样他们做 XOR 运算的时候才有可能使 c 的前缀更大。
这里我们创建一个mask去判断每个二进制数字的前缀。mask是由10000…000到11111…111(32位)。随着mask的变大,比如从10000到11111(我这里暂时用5位表示),如果nums里面的数字跟mask比较的时候,他们的前缀里面也包含1的话,在做AND运算的时候,会得到这些前缀。此时我们在找前缀的同时,把这些前缀存到一个hashset里。每一个mask都会与input数组里面的每一个数字做AND运算,得到一个前缀,set里最终留下的是所有unique的前缀。
此时再创建一个mask叫做temp,也是从1 - 31逐渐增加1的个数。这里我理解temp就是在猜最后XOR的结果,然后往回套,看hashset是否存在两个数字a和b满足a ^ b = temp。判断的方式是对于hashset里面的某个元素a,如果满足a ^ temp = b,同时b也在hashset中,则表明找到了一个有效的temp。
复杂度
时间O(n)
空间O(n)
代码
Java实现
1 |
|
二刷我再提供一个前缀树的做法,我参考了这个帖子。难得官方写了一篇好帖子。
对于一个数字,我们可以用一个长度为 32 的二进制数字来表达。如果我们从高位到低位把每个位置上的 digit 放入一棵字典树(这里其实只有 0 和 1 两种情况所以是二叉树),那么对于某个数字 x 而言,如果我要找和他做 XOR 操作结果更大的数字,那么我只要尽量去找每一个 digit 上都与 x 不同的数字即可,这个概念平移到这棵二叉树里,就是在每个 node 上,如果 x 在这个位置上是 1,那么我就尽量去往 0 的那个分支上走,反之如果 x 在这个位置上是 0,那么我就尽量去往 1 的那个分支上走。
具体到这道题,对于某个数字 nums[i],我会先把前 i - 1 个数字的二进制表达都放到字典树里,那么我在遍历当前这个数字 nums[i] 的时候,我就可以找到与他做 XOR 运算能得到的最大结果是多少。
时间O(nlogc) - n 个数字,每个数字被插入字典树的复杂度为O(logc)
空间O(nlogc)
Java实现
1 |
|