[LeetCode] 421. 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 result of ai XOR aj, where 0 ≤ i, j < 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
LeetCode All in One 题目讲解汇总(持续更新中...)
[LeetCode] 421. Maximum XOR of Two Numbers in an Array 数组中异或值最大的两个数字的更多相关文章
- [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 ...
- 421 Maximum XOR of Two Numbers in an Array 数组中两个数的最大异或值
给定一个非空数组,数组中元素为 a0, a1, a2, … , an-1,其中 0 ≤ ai < 231 .找到 ai 和aj 最大的异或 (XOR) 运算结果,其中0 ≤ i, j < ...
- [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 ...
- leetcode 421.Maximum XOR of Two Numbers in an Array
题目中给定若干个数,然后任意选定两个数使得其异或值最大. 先利用样例中的: 3 10 5 25 2 8 这些数转换为二进制来看的话那么是先找到最高位的1然后与数组中其他的数相与后的数值保存到set中去 ...
- 【LeetCode】421. Maximum XOR of Two Numbers in an Array 解题报告(Python & C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 解题方法 依次遍历每一位 前缀树 日期 题目地址:https://lee ...
- 【leetcode】421. Maximum XOR of Two Numbers in an Array
题目如下: 解题思路:本题的难点在于O(n)的复杂度.为了减少比较的次数,我们可以采用字典树保存输入数组中所有元素的二进制的字符串.接下来就是找出每个元素的异或的最大值,把需要找最大值的元素转成二进制 ...
- 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 ...
- 421. Maximum XOR of Two Numbers in an Array
这题要求On时间复杂度完成, 第一次做事没什么思路的, 答案网上有不贴了, 总结下这类题的思路. 不局限于这个题, 凡是对于这种给一个 数组, 求出 xxx 最大值的办法, 可能上来默认就是dp, ...
- 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 ...
随机推荐
- liunx下安装mysql-5.7.25-linux-glibc2.12-x86_64.tar.gz
1.解压准备一个赶紧的环境,然后安装mysql. 2.cd到/usr/local/目录下,修改文件名为mysql 修改完目录名以后我们cd到mysql下,建立一个data目录命令:cd mysql/ ...
- 记录一个终端入网小助手的bug
背景:技术leader拿到一台超薄笔记本,系统标准化安装,笔记本一开机风扇嗡嗡响,键盘也开始发烫,资源占用排名前三的进程都是终端管理软件,一下子就找上门了.处理:进程分析发现异常,卸载入网小助手后恢复 ...
- 基于直接缓冲区和非直接缓冲区的javaIO文件操作
基本概念: 1. 非直接缓冲区: 指的是通过jvm来缓存数据的,应用程序要读取本地数据要经历从本地磁盘到物理内存,然后copy到jvm中,然后再通过流的方式读取到应用程序中,写的操作正好与之相反. ...
- 微信分享网页时自定义缩略图和简介(.net版本)
要实现微信分享网页时自定义缩略图和简介,需开发者在公众平台网站中创建公众号.获取接口权限后,通过微信JS-SDK的分享接口,来实现微信分享功能. 下面来说明实现步骤. 第一部分 准备步骤 步骤一:注册 ...
- Winform中设置和获取DevExpress的RadioGroup的选中项的value值
场景 Winform中实现读取xml配置文件并动态配置ZedGraph的RadioGroup的选项: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article ...
- C# 多线程处理List数据
代码思路 将要处理的数据放到ConcurrentQueue中,然后开启多个线程去处理数据,处理完成后,再到队列中获取下一个待处理数据. ConcurrentQueue 表示线程安全的先进先出 (FIF ...
- MySQL基础(四)(子查询与链接)
1.子查询简介 其中,所谓的“外层查询”并不是指“查找”,指的是所有SQL语句的统称:结构化查询语言(Structured Query Language),简称SQL. : 2.由比较运算符引发的子查 ...
- Lucene搜索/索引过程笔记
lucene索引文档过程: > 初始化IndexWriter > 构建Document > 调用IndexWriter.addDocument执行写入 > 初始化Documen ...
- Web Api 模型绑定 二
[https://docs.microsoft.com/zh-cn/aspnet/core/web-api/?view=aspnetcore-2.2] 1.ApiController属性使模型验证错误 ...
- wpf 窗体添加背景图片
方法一:xaml中:<控件> <控件.Background><ImageBrush ImageSource="/WpfApplication1;compon ...