数组中两数的最大异或值

给定一个非空数组,数组中元素为 a0, a1, a2, … , an-1,其中 0 ≤ ai < 231 

找到 ai 和a最大的异或 (XOR) 运算结果,其中0 ≤ i,j < n

你能在O(n)的时间解决这个问题吗?

示例:

输入: [3, 10, 5, 25, 2, 8]

输出: 28

解释: 最大的结果是 5 ^ 25 = 28.

这道题是一道典型的位操作Bit Manipulation的题目,我开始以为异或值最大的两个数一定包括数组的最大值,但是OJ给了另一个例子{10,23,20,18,28},这个数组的异或最大值是10和20异或,得到30。那么只能另辟蹊径,正确的做法是按位遍历,题目中给定了数字的返回不会超过231,那么最多只能有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     11001     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      1010     101     11001     10      1000

当 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      1010     101     11001     10      1000

当 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

11      1010     101     11001     10      1000

当 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'异或'得到。参见代码如下:

 import java.util.HashSet;

 class Solution {
public static int findMaximumXOR(int[] nums) {
int max = 0;
int mask = 0;
for (int i = 31; i >= 0; i--) {
// 从最高位试着找nums的前缀
mask = mask | (1 << i);
HashSet<Integer> set = new HashSet<Integer>();
for (int num : nums) {
set.add(mask & num);
}
//判断最大异或结果的当前位是否为1
int temp=max|(1<<i);
for (int prefix: set){
if (set.contains(prefix^temp)) {
max=temp;
}
}
}
return max;
}
}

Leetcode 421.数组中两数的最大异或值的更多相关文章

  1. 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 ...

  2. Java实现 LeetCode 421 数组中两个数的最大异或值

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

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

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

  4. [Swift]LeetCode421. 数组中两个数的最大异或值 | 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 ...

  5. LeetCode 数组中两个数的最大异或值

    题目链接:https://leetcode-cn.com/problems/maximum-xor-of-two-numbers-in-an-array/ 题目大意: 略. 分析: 字典树 + 贪心. ...

  6. 求数组中两数之和等于target的两个数的下标

    给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,你不能重复利用这个数组中同样的元 ...

  7. [LeetCode]1464. 数组中两元素的最大乘积

    给你一个整数数组 nums,请你选择数组的两个不同下标 i 和 j,使 (nums[i]-1)*(nums[j]-1) 取得最大值. 请你计算并返回该式的最大值. 示例 1: 输入:nums = [3 ...

  8. python实现给定一个数和数组,求数组中两数之和为给定的数

    给定一个整数数组和一个目标值,找出数组中和为目标值的两个数.你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用. 示例: 给定 nums = [2, 7, 11, 15], target = ...

  9. leetcode-421-数组中两个数的最大异或值*(前缀树)

    题目描述: 方法一: class Solution: def findMaximumXOR(self, nums: List[int]) -> int: root = TreeNode(-1) ...

随机推荐

  1. npm使用快速的安装源(nrm)

    安装 npm install nrm --global 使用 nrm ls 切换安装源 nrm use taobao 测速 nrm test npm 参考地址:http://codingdict.co ...

  2. SaaS 系统架构设计经验总结

    2B SaaS系统最近几年都很火.很多创业公司都在尝试创建企业级别的应用 cRM, HR,销售, Desk SaaS系统.很多SaaS创业公司也拿了大额风投.毕竟SaaS相对传统软件的优势非常明显. ...

  3. 一键部署启动MySQL数据库服务器

    https://market.azure.cn/Vhd/Show?vhdId=9858&version=14359 产品详情 产品介绍MySQL是一个真正的多用户.多线程SQL数据库服务器.S ...

  4. java代码关闭tomcat程序

    1.通过java代码实现tomcat的关闭 2.tomcatStop.java package test; import java.io.BufferedReader; import java.io. ...

  5. IT的学习部落(持续更新)

    1.易百教程 - 专注于IT教程和实例     http://www.yiibai.com/ 2.站长特效 - js特效      http://www.zzjs.net/ 3.酷站-享受编程和技术所 ...

  6. FMDB浅析(思想)

    http://www.cnblogs.com/OTgiraffe/p/5931800.html 一.FMDB介绍 FMDB是一种第三方的开源库,FMDB就是对SQLite的API进行了封装,加上了面向 ...

  7. JS的闭包、高阶函数、柯里化

    本文原链接:https://cloud.tencent.com/developer/article/1326958 https://cloud.tencent.com/developer/articl ...

  8. LiteIDE 错误: 进程无法启动

    问题 运行 01_hello.go,提示以下错误 新建文件夹().exe [C:/Users/Administrator/Desktop/新建文件夹()] 错误: 进程无法启动. 原因 工程目录名不能 ...

  9. 使用 CFile 的子类 CStdioFile 的注意事项

    目前为止只用到了 ReadString,也了解了一下 WriteString. 由于程序需要,本来程序中是用的CFile, 但是需要逐行读取文件数据,所以谷歌找到了 ReadString 类 —— 继 ...

  10. JavaScript中数组的使用

    ---恢复内容开始--- 创建数组 1,通过 var arr1 = [1,2,3] 2通过使用 var arr2 = new Array(1,2,3) 在这里创造的数组实际上都是一个对象,然后把对象的 ...