Given a non-empty array of numbers, a0, a1, a2, … , an-1, where 0 ≤ ai < 231.

Find the maximum result of ai XOR aj, where 0 ≤ ij < n.

Could you do this in O(n) runtime?

Example:

Input: [3, 10, 5, 25, 2, 8]

Output: 28

Explanation: The maximum result is 5 ^ 25 = 28.

这道题是一道典型的位操作 Bit Manipulation 的题目,我开始以为异或值最大的两个数一定包括数组的最大值,但是 OJ 给了另一个例子 {10,23,20,18,28},这个数组的异或最大值是 10 和 20 异或,得到 30。那么只能另辟蹊径,正确的做法是按位遍历,题目中给定了数字的返回不会超过 2^31,那么最多只能有 32 位,我们用一个从左往右的 mask,用来提取数字的前缀,然后将其都存入 HashSet 中,我们用一个变量t,用来验证当前位为1再或上之前结果 res,看结果和 HashSet 中的前缀异或之后在不在 HashSet 中,这里用到了一个性质,若 a^b=c,那么 a=b^c,因为t是我们要验证的当前最大值,所以我们遍历 HashSet 中的数时,和t异或后的结果仍在 HashSet 中,说明两个前缀可以异或出t的值,所以我们更新 res 为t,继续遍历,如果上述讲解不容易理解,那么建议自己带个例子一步一步试试,并把每次循环中 HashSet 中所有的数字都打印出来,基本应该就能理解了,算了,还是博主带着大家来看题目中给的例子吧:

3        10        5        25        2        8

11      1010     101     1001     10      1000

我们观察这些数字最大的为 25,其二进制最高位在 i=4 时为1,那么我们的循环 [31, 5] 之间是取不到任何数字的,所以不会对结果 res 有任何影响。

当 i=4 时,我们此时 mask 为前 28 位为 ‘1’ 的二进制数,跟除 25 以外的任何数相‘与’,都会得到0。 然后跟 25 的二进制数 10101 相‘与’,得到二进制数 10000,存入 HashSet 中,那么此时 HashSet 中就有0和16两个数字。此时我们的t为结果 res(此时为0)‘或’上二进制数 10000,得到二进制数 10000。然后我们遍历 HashSet,由于 HashSet 是无序的,所以我们会取出0和 16 中的其中一个,如果 prefix 取出的是0,那么 t=16 ‘异或’上0,还等于 16,而 16 是在 HashSet 中存在的,所以此时结果 res 更新为 16,然后 break 掉遍历 HashSet 的循环。实际上 prefix 先取 16 的话也一样,那么 t=16 ‘异或’上 16,等于0,而0是在 HashSet 中存在的,所以此时结果 res 更新为 16,然后 break 掉遍历 HashSet 的循环。

3        10        5        25        2        8

11      010     101     001     10      000

当 i=3 时,我们此时 mask 为前 29 位为 ‘1’ 的二进制数,如上所示,跟数字 3,5,2 中任何一个相‘与’,都会得到0。然后跟 10 的二进制数 1010,或跟8的二进制数 1000 相‘与’,都会得到二进制数 1000,即8。跟 25 的二进制数 11001 相‘与’,会得到二进数 11000,即 24,存入 HashSet 中,那么此时 HashSet 中就有 0,8,和 24 三个数字。此时我们的t为结果 res(此时为 16)‘或’上二进制数 1000,得到二进制数 11000,即 24。此时遍历 HashSet 中的数,当 prefix 取出0,那么 t=24 ‘异或’上0,还等于 24,而 24 是在 HashSet 中存在的,所以此时结果 res 更新为 24,然后 break 掉遍历 HashSet 的循环。大家可以尝试其他的数,当 prefix 取出 24,其实也可以更新结果 res 为 24 的。但是8就不行啦,因为 HashSet 中没有 16。不过无所谓了,我们只要有一个能更新结果 res 就可以了。

3        10        5        25        2        8

11      10     01     01     10      00

当 i=2 时,我们此时 mask 为前 30 位为 ‘1’ 的二进制数,如上所示,跟3的二进制数 11 相‘与’,会得到二进制数0,即0。然后跟 10 的二进制数 1010 相‘与’,会得到二进制数 1000,即8。然后跟5的二进制数 101 相‘与’,会得到二进制数 100,即4。然后跟 25 的二进制数 11001 相‘与’,会得到二进制数 11000,即 24。跟数字2和8相‘与’,分别会得到0和8,跟前面重复了。所以最终 HashSet 中就有0,4,8,和 24 这四个数字。此时我们的t为结果 res(此时为 24)‘或’上二进制数 100,得到二进制数 11100,即 28。那么就要验证结果 res 能否取到28。我们遍历 HashSet,当 prefix 取出0,那么 t=28 ‘异或’上0,还等于 28,但是 HashSet 中没有 28,所以不行。当 prefix 取出4,那么 t=28 ‘异或’上二进制数 100,等于 24,在 HashSet 中存在,Bingo!结果res更新为 28。其他的数可以不用试了。

3        10        5        25        2        8

1      0     1     1     0      0

当 i=1 时,我们此时 mask 为前 31 位为 ‘1’ 的二进制数,如上所示,每个数与 mask 相‘与’后,我们 HashSet 中会有 2,4,8,10,24 这五个数。此时我们的t为结果 res(此时为 28)‘或’上二进制数 10,得到二进制数 11110,即 30。那么就要验证结果 res 能否取到 30。我们遍历 HashSet,当 prefix 取出2,那么 t=30 ‘异或’上2,等于 28,但是 HashSet 中没有 28,所以不行。当 prefix 取出4,那么 t=30 ‘异或’上4,等于 26,但是 HashSet 中没有 26,所以不行。当 prefix 取出8,那么 t=30 ‘异或’上8,等于 22,但是 HashSet 中没有 22,所以不行。当 prefix 取出 10,那么 t=30 ‘异或’上 10,等于 20,但是 HashSet 中没有 20,所以不行。当 prefix 取出 24,那么 t=30 ‘异或’上 24,等于6,但是 HashSet 中没有6,所以不行。遍历完了 HashSet 所有的数,结果 res 没有被更新,还是 28。

3        10        5        25        2        8

11      1010     101     11001     10      1000

当 i=0 时,我们此时 mask 为前 32 位为 ‘1’ 的二进制数,如上所示,每个数与 mask 相‘与’后,我们 HashSet 中会有 2,3,5,8,10,25 这六个数。此时我们的t为结果 res(此时为 28)‘或’上二进制数1,得到二进制数 11101,即 29。那么就要验证结果 res 能否取到 29。取出 HashSet 中每一个数字来验证,跟上面的验证方法相同,这里博主偷懒就不写了,最终可以发现,结果 res 无法被更新,还是 28,所以最终的结果就是 28。

综上所述,我们来分析一下这道题的核心。我们希望用二进制来拼出结果的数,最终结果 28 的二进制数为 11100,里面有三个 ‘1’,我们来找一下都是谁贡献了这三个 ‘1’?在 i=4 时,数字 25 贡献了最高位的 ‘1’,在 i=3 时,数字 25 贡献了次高位的 ‘1’,在 i=2 时,数字5贡献了第三位的 ‘1’。而一旦某个数贡献了 ‘1’,那么之后在需要贡献 ‘1’ 的时候,此数就可以再继续贡献 ‘1’。而一旦有两个数贡献了 ‘1’ 后,那么之后的 ‘1’ 就基本上只跟这两个数有关了,其他数字有 ‘1’ 也贡献不出来。验证方法里使用了前面提到的性质,a ^ b = t,如果t是所求结果话,我们可以先假定一个t,然后验证,如果 a ^ t = b 成立,说明该t可以通过a和b‘异或’得到。参见代码如下:

class Solution {
public:
int findMaximumXOR(vector<int>& nums) {
int res = , mask = ;
for (int i = ; i >= ; --i) {
mask |= ( << i);
unordered_set<int> s;
for (int num : nums) {
s.insert(num & mask);
}
int t = res | ( << i);
for (int prefix : s) {
if (s.count(t ^ prefix)) {
res = t;
break;
}
}
}
return res;
}
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/421

参考资料:

https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/

https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/discuss/130427/()-92

https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/discuss/91049/Java-O(n)-solution-using-bit-manipulation-and-HashMap

https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/discuss/91064/C%2B%2B-22-ms-beats-99.5-array-partitioning-similar-to-quick-sort

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] 421. Maximum XOR of Two Numbers in an Array 数组中异或值最大的两个数字的更多相关文章

  1. [LeetCode] Maximum XOR of Two Numbers in an Array 数组中异或值最大的两个数字

    Given a non-empty array of numbers, a0, a1, a2, … , an-1, where 0 ≤ ai < 231. Find the maximum re ...

  2. 421 Maximum XOR of Two Numbers in an Array 数组中两个数的最大异或值

    给定一个非空数组,数组中元素为 a0, a1, a2, … , an-1,其中 0 ≤ ai < 231 .找到 ai 和aj 最大的异或 (XOR) 运算结果,其中0 ≤ i,  j < ...

  3. [LeetCode] 421. Maximum XOR of Two Numbers in an Array(位操作)

    传送门 Description Given a non-empty array of numbers, a0, a1, a2, … , an-1, where 0 ≤ ai < 231. Fin ...

  4. leetcode 421.Maximum XOR of Two Numbers in an Array

    题目中给定若干个数,然后任意选定两个数使得其异或值最大. 先利用样例中的: 3 10 5 25 2 8 这些数转换为二进制来看的话那么是先找到最高位的1然后与数组中其他的数相与后的数值保存到set中去 ...

  5. 【LeetCode】421. Maximum XOR of Two Numbers in an Array 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 解题方法 依次遍历每一位 前缀树 日期 题目地址:https://lee ...

  6. 【leetcode】421. Maximum XOR of Two Numbers in an Array

    题目如下: 解题思路:本题的难点在于O(n)的复杂度.为了减少比较的次数,我们可以采用字典树保存输入数组中所有元素的二进制的字符串.接下来就是找出每个元素的异或的最大值,把需要找最大值的元素转成二进制 ...

  7. 421. Maximum XOR of Two Numbers in an Array——本质:利用trie数据结构查找

    Given a non-empty array of numbers, a0, a1, a2, - , an-1, where 0 ≤ ai < 231. Find the maximum re ...

  8. 421. Maximum XOR of Two Numbers in an Array

    这题要求On时间复杂度完成, 第一次做事没什么思路的, 答案网上有不贴了, 总结下这类题的思路. 不局限于这个题, 凡是对于这种给一个  数组,  求出 xxx 最大值的办法, 可能上来默认就是dp, ...

  9. LeetCode 421. 数组中两个数的最大异或值(Maximum XOR of Two Numbers in an Array) 71

    421. 数组中两个数的最大异或值 421. Maximum XOR of Two Numbers in an Array 题目描述 给定一个非空数组,数组中元素为 a0, a1, a2, - , a ...

随机推荐

  1. flink solt,并行度

    转自:https://www.jianshu.com/p/3598f23031e6 简介 Flink运行时主要角色有两个:JobManager和TaskManager,无论是standalone集群, ...

  2. 【Zabbix】zabora批量部署

    zabora简化批量部署 目的:简化部署zabora,批量监控数据库的常用指标 1 数据库用户赋权 上传cre_arp_monitor.sh,并且部署用户. [root@oradb ~]# chown ...

  3. 使用 jQuery.TypeAhead 让文本框自动完成 (四)(自定义模板)

    项目地址:https://github.com/twitter/typeahead.js 直接贴代码了: @section headSection { <script type="te ...

  4. Java学习——BigInteger类和BigDecimal类

    Java学习——BigInteger类和BigDecimal类 摘要:本文主要学习了用于大数字运算的BigInteger类和BigDecimal类. 部分内容来自以下博客: https://www.c ...

  5. ASP.NET Core系列:依赖注入

    1. 控制反转(IoC) 控制反转(Inversion of Control,IoC),是面向对象编程中的一种设计原则,用来降低代码之间的耦合度. 1.1 依赖倒置 依赖原则: (1)高层次的模块不应 ...

  6. jvm默认的并行垃圾回收器和G1垃圾回收器性能对比

    http://www.importnew.com/13827.html 参数如下: JAVA_OPTS="-server -Xms1024m -Xmx1024m -Xss256k -XX:M ...

  7. vue学习指南:第三篇(详细) - vue的生命周期

    今天小编给大家详细讲解一下 vue 的生命周期.希望大家多多指教,哪里有遗漏的地方,也请大家指点出来 谢谢. 一. 怎么理解 Vue 的生命周期的? 生命周期:从无到有,到到无的一个过程.Vue的生命 ...

  8. 通过jQuery给<li>绑定点击事件

    背景:有个需求需要js中实现li标签的事件绑定,li通过在ajax请求中动态添加,按照常理,使用jQuery,可以使用以下方法绑定点击事件: $(function(){  commonAjaxCall ...

  9. EM算法直观认识

    Expectation Maximization, 字面翻译为, "最大期望". 我个人其实一直都不太理解EM算法, 从我个人的渊源来看, 之前数理统计里面的参数估计, 也是没有太 ...

  10. lua 的 cjson 安装,使用

    1. 背景: 虚拟机安装的luajit  没有 cjson 库,就不能对 table 进行 编码操作,手动安装一个. 2. 安装: cjson下载地址:http://www.kyne.com.au/~ ...