[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 ...
随机推荐
- PHP-内嵌foreach的巧妙优化
1.没有想好使用什么话题做开场白,不说多废话直接上代码了. 这是tp5.1的api接口里的代码,$user_list 是二维数组只有 1104一维数组数据 $friend_list 也是二维数组, ...
- LeetCode 234:回文链表 Palindrome Linked List
请判断一个链表是否为回文链表. Given a singly linked list, determine if it is a palindrome. 示例 1: 输入: 1->2 输出: ...
- tensorflow: arg_scope()
with arg_scope(): 1.允许我们设定一些共享参数,并将其进行保存,必要时还可以嵌套覆盖 2.在指定的函数调用时,可以将一些默认参数塞进去. 接下来看一个tensorflow自带的例子. ...
- SQL Server 通过“with as”方法查询树型结构
一.with as 公用表表达式 类似VIEW,但是不并没有创建对象,WITH AS 公用表表达式不创建对象,只能被后随的SELECT语句,其作用: 1. 实现递归查询(树形结构) 2. 可以在一个 ...
- 汇总Anaconda与ROS冲突解决方法
汇总一下在网上找到的Anaconda与ROS冲突解决方法,如果还有其他人找到其他方法,欢迎留言. anaconda和ros的安装就不介绍了. 1. 在某视频网站上一个印度小哥提出的方法 安装完成后,在 ...
- Spring Cloud Feign 服务消费调用(三)
序言 Spring Cloud Netflix的微服务都是以HTTP接口的形式暴露的,所以可以用Apache的HttpClient或Spring的RestTemplate去调用 而Feign是一个使用 ...
- golang中,slice的几个易混淆点
slice在golang中是最常用的类型,一般可以把它作为数组使用,但是比数组要高效呀.不过,我感觉这个东西用的不好坑太多了.还是需要了解下他底层的实现 slice的结构定义 type slice s ...
- Java编程基础——标识符和关键字
Java编程基础——标识符和关键字 摘要:本文主要介绍标识符和关键字. 标识符 是什么 Java语言中,为各种变量.方法.类和包等起的名字,统统称之为Java标识符. 命名规则 ◆ 应以字母.下划线. ...
- Python基础18
“为什么有列表,还要元组?” 1. 元组可看成是简单的对象组合,而列表是随时间改变的数据集合. 2. 元组的不可变特性提供了某种完整性,确保元组不会被另一个引用来修改.类似于其它语言中的常数声明.
- mssql like 优化
SqlServer中like 的查询一般我们都不推荐,但是当数据库某个字段的值是用分隔符区分的多个链接字符,比如,12,11,23等这样的类型.可能我们需要判断是否包含12. 这个时候我们想到的当然是 ...