今天分享的题目来源于 LeetCode 第 421 号问题:数组中两个数的最大异或值。在 异或 这个知识点里面属于一个中高难度的题目。

题目描述

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

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

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

示例:

输入: [3, 10, 5, 25, 2, 8]输出: 28解释: 最大的结果是 5 ^ 25 = 28.

题目解析

解决这个问题,我们首先需要利用异或运算的一个性质:

如果 a ^ b = c 成立,那么a ^ c = b 与 b ^ c = a 均成立。

如果有三个数,满足其中两个数的异或值等于另一个值,那么这三个数的顺序可以任意调换

  • 那么如何理解这个性质呢?因为异或运算其实就是二进制下不进位的加法,你不妨自己举几个例子,在草稿纸上验证一下。

那这个性质如何应用到本题呢?

这道题找最大值的思路是这样的:因为两两异或可以得到一个值,在所有的两两异或得到的值中,一定有一个最大值,我们推测这个最大值应该是什么样的?即根据“最大值”的存在性解题(一定存在)。在这里要强调一下:

我们只用关心这个最大的异或值需要满足什么性质,进而推出这个最大值是什么,而不必关心这个异或值是由哪两个数得来的。

(上面这句话很重要,如果读者一开始看不明白下面的思考,不妨多看几遍我上面写的这句话。)

于是有如下思考:

1、二进制下,我们希望一个数尽可能大,即希望越高位上越能够出现“1”,这样这个数就是所求的最大数,这是贪心算法的思想。

2、于是,我们可以从最高位开始,到最低位,首先假设高位是 “1”,把这 n 个数全部遍历一遍,看看这一位是不是真的可以是“1”,否则这一位就得是“0”,判断的依据是上面“异或运算的性质”,即下面的第 3 点;

3、如果 a ^ b = max 成立 ,max 表示当前得到的“最大值”,那么一定有 max ^ b = a 成立。我们可以先假设当前数位上的值为 “1”,再把当前得到的数与这个 n 个数的 前缀(因为是从高位到低位看,所以称为“前缀”)进行异或运算,放在一个哈希表中,再依次把所有 前缀 与这个假设的“最大值”进行异或以后得到的结果放到哈希表里查询一下,如果查得到,就说明这个数位上可以是“1”,否则就只能是 0(看起来很晕,可以看代码理解)。

一种极端的情况是,这 n 个数在某一个数位上全部是 0 ,那么任意两个数异或以后都只能是 0,那么假设当前数位是 1 这件事情就不成立。

4、如何得到前缀,可以用掩码(mask),掩码可以进行如下构造,将掩码与原数依次进行“与”运算,就能得到前缀。

1000000000000000000000000000000011000000000000000000000000000000111000000000000000000000000000001111000000000000000000000000000011111000000000000000000000000000111111000000000000000000000000001111111000000000000000000000000011111111000000000000000000000000111111111000000000000000000000001111111111000000000000000000000011111111111000000000000000000000111111111111000000000000000000001111111111111000000000000000000011111111111111000000000000000000111111111111111000000000000000001111111111111111000000000000000011111111111111111000000000000000111111111111111111000000000000001111111111111111111000000000000011111111111111111111000000000000111111111111111111111000000000001111111111111111111111000000000011111111111111111111111000000000111111111111111111111111000000001111111111111111111111111000000011111111111111111111111111000000111111111111111111111111111000001111111111111111111111111111000011111111111111111111111111111000111111111111111111111111111111001111111111111111111111111111111011111111111111111111111111111111

以题目中的数组 [3, 10, 5, 25, 2, 8] 为例,下面讲解这个最大的两两异或值是如何得到的,这里为了方便演示,只展示一个数二进制的低 8 位。

图片演示

LeetCode 第 421 题:数组中两个数的最大异或值-1

LeetCode 第 421 题:数组中两个数的最大异或值-2

LeetCode 第 421 题:数组中两个数的最大异或值-3

LeetCode 第 421 题:数组中两个数的最大异或值-4

LeetCode 第 421 题:数组中两个数的最大异或值-5

LeetCode 第 421 题:数组中两个数的最大异或值-6

代码实现

Python 代码:

class Solution: def findMaximumXOR(self, nums: List[int]) -> int: res = 0 mask = 0 for i in range(31, -1, -1): mask |= (1 << i) # 当前得到的所有前缀都放在这个哈希表中 s = set() for num in nums: s.add(mask & num) # 先“贪心地”假设这个数位上是 “1” ,如果全部前缀都看完,都不符合条件,这个数位上就是 “0”  temp = res | (1 << i) for prefix in s: if temp ^ prefix in s: res = temp break return res

Java 代码:

import java.util.HashSet;import java.util.Set;public class Solution { // 先确定高位,再确定低位(有点贪心算法的意思),才能保证这道题的最大性质 // 一位接着一位去确定这个数位的大小 // 利用性质:a ^ b = c ,则 a ^ c = b,且 b ^ c = a public int findMaximumXOR(int[] nums) { int res = 0; int mask = 0; for (int i = 31; i >= 0; i--) { // 注意点1:注意保留前缀的方法,mask 是这样得来的 // 用异或也是可以的 mask = mask ^ (1 << i); mask = mask | (1 << i); // System.out.println(Integer.toBinaryString(mask)); Set set = new HashSet<>(); for (int num : nums) { // 注意点2:这里使用 & ,保留前缀的意思(从高位到低位) set.add(num & mask); } // 这里先假定第 n 位为 1 ,前 n-1 位 res 为之前迭代求得 int temp = res | (1 << i); for (Integer prefix : set) { if (set.contains(prefix ^ temp)) { res = temp; break; } } } return res; } public static void main(String[] args) { int[] nums = {3, 10, 5, 25, 2, 8}; Solution2 solution2 = new Solution2(); int maximumXOR = solution2.findMaximumXOR(nums); System.out.println(maximumXOR); }}

复杂度分析

  • 时间复杂度:(),把整个数组看了 32次,即 (32)=()。
  • 空间复杂度:(1),使用了一个哈希表,这个哈希表最多存 32 个前缀,(32)=(1)。

arrays中copyof复制两个数组_异或的魅力!图解「数组中两个数的最大异或值」相关推荐

  1. arrays中copyof复制两个数组_数据结构与算法(3)数组

    前言 数组(Array)是一种线性表数据结构,利用一组连续的内存空间,存储一组具有相同类型的数据. 概念介绍 首先我们说一下什么是线性表,线性表就是数据排成一条线的数据结构,每个线性表最多只有前和后两 ...

  2. arrays中copyof复制两个数组_数组,及二维数组

    1.1 命令行参数(C) 在程序运行过程中,可以向应用程序传递一些参数,这些参数称为命名行参数. public 命令行参数以字符串的形式传入args数组中.可以一次传递0-多个参数,以空格分割. 如果 ...

  3. arrays中copyof复制两个数组_Java教程分享之数组知识梳理

    Java是一门面向对象编程语言,具有简单易用.功能强大的特征.数组是同类型数据的有序集合,在Java中是引用数据类型,引用数据类型值都存储在堆中.有很多新手初学Java数组觉得难度大,接下来就给大家简 ...

  4. arrays中copyof复制两个数组_Java的数组初识和拷贝用法

    方法重载:方法名称相同,参数列表不同. 不能有两个名字相同.参数类型相同,返回值不同的方法. 在进行方法重载时,方法的返回值一定相同!!! 方法递归特点: 1.必须有结束条件 2.每次递归处理时,一定 ...

  5. arrays中copyof复制两个数组_Python数组切片中的复制与否问题-list篇

    说到Python中数组的切片操作,稍有了解的想必都不陌生.以Python的内置数据类型list(列表)为例, L = [5, 2, 0, 1, 3, 1, 4] L1 = L[3:7] 我们称L[3: ...

  6. arrays中copyof复制两个数组_C语言100题集合026-使用指针交换两个数组中的最大值

    系列文章<C语言经典100例>持续创作中,欢迎大家的关注和支持. 喜欢的同学记得点赞.转发.收藏哦- 后续C语言经典100例将会以pdf和代码的形式发放到公众号 欢迎关注:计算广告生态 即 ...

  7. java基数排序 数组_万字长文带你掌握Java数组与排序,代码实现原理都帮你搞明白!...

    查找元素索引位置 基本查找 根据数组元素找出该元素第一次在数组中出现的索引 public class TestArray1 { public static void main(String[] arg ...

  8. python 结构体数组_关于python:将结构化数组转换为常规NumPy数组

    我认为答案将非常明显,但目前看不到. 如何将记录数组转换回常规ndarray? 假设我有以下简单的结构化数组: x = np.array([(1.0, 4.0,), (2.0, -1.0)], dty ...

  9. c语言随机生成整数存放一维数组_文科生学 Python 系列 7: Numpy 数组/索引和切片...

    第四课:本课内容: • 0. 导入 NumPy 包 • 1. 创建 NumPy 数组 • 2. 索引和切片 • 3. 读取文件 • 4. 布尔型索引 • 5. 数组的运算 • 6. 常用函数举例 Nu ...

最新文章

  1. 从锁的原理到构建分布式锁
  2. 微信卡券 - 微信公众平台 整理笔记
  3. python vars name报错_Python vars()全局名称错误
  4. jQuery 表单应用:全选/取消全选,表单验证,网页选项卡切换
  5. linux如何删除密钥链接,如何在不创建新密钥的情况下删除SSH密钥的密码短语?...
  6. Jmeter安装及配置
  7. android m4a转mp3格式转换,音频提取格式转换app
  8. SaaSpace:10种最佳免费密码管理器软件
  9. 项目实训 : gitlab 配置ssh key后不生效问题
  10. Ubuntu Linux,及Python matplot,安装Times New Roman等字体,让图标签可以用Times New Roman等字体
  11. python爬取bilibili数据_用 Python 抓取 bilibili 弹幕并分析!
  12. 计算机专业开题报告这么写,有效有用还能过
  13. JQuery使用及基础原理解析相关笔记(三)
  14. 消息队列的消息积压解决办法
  15. 一文说尽 MySQL 优化原理
  16. 超详细 PHP 开发环境配置:WampServer+ZendStudio+XDebug
  17. Android开发必备(干货源码放送大)
  18. php 判断来源 微信客户端_使用PHP判断是否为微信、支付宝等移动设备访问代码...
  19. three.js 实现辉光(原生JS)
  20. 阿里云的oss看这一篇就够,手把手教你,上传下载速度再也不用愁了,个人网站速度太慢,一定要看!

热门文章

  1. python 生意参谋_GitHub - iOSDevLog/sycm: 生意参谋
  2. UpdatePanel中用后台调用Javascript
  3. SpringMVC和SpringBoot的拦截器 HandlerInterceptor 入门
  4. android 中intent跳转是灰色的,没有效果,显示intent = null
  5. 二分查找之搜索插入位置
  6. 日常生活小技巧 -- UltraEdit复制16进制数据
  7. S5PV210开发 -- I2C 你知道多少?(二)
  8. UNIX再学习 -- 错误和警告
  9. 【译】Diving Into The Ethereum VM Part 4 - How To Decipher A Smart Contract Method Call
  10. gRPC客户端创建和调用原理解析