LeetCode421. 数组中两个数的最大异或值
Posted Zephyr丶J
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode421. 数组中两个数的最大异或值相关的知识,希望对你有一定的参考价值。
421. 数组中两个数的最大异或值
2021.5.16 每日一题
题目描述
给你一个整数数组 nums ,返回 nums[i] XOR nums[j] 的最大运算结果,其中 0 ≤ i ≤ j < n 。
进阶:你可以在 O(n) 的时间解决这个问题吗?
示例 1:
输入:nums = [3,10,5,25,2,8]
输出:28
解释:最大运算结果是 5 XOR 25 = 28.
示例 2:
输入:nums = [0]
输出:0
示例 3:
输入:nums = [2,4]
输出:6
示例 4:
输入:nums = [8,10,2]
输出:10
示例 5:
输入:nums = [14,70,53,83,49,91,36,80,92,51,66,70]
输出:127
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-xor-of-two-numbers-in-an-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
挺难的
看到题以后想到如何尽可能让每一位都为1,而且是越高位越好,这样结果就能最大,然后就想怎么确定每一位为1,只能想到让数组中高位为1的大数与其他异或找最大的,达不到O(n)的复杂度
然后去看题解:
哈希表
第一个方法,一个数字有31个二进制位,从高到低确定每一位是否能为1
那么如何确定每一位是否能为1呢,31一个二进制位逐一判断
先将每一个数移动k位放入哈希表中,然后对于之前得到的最大数 x,判断能否与数组中的数异或,得到使当前第k + 1位为1的结果
class Solution {
public int findMaximumXOR(int[] nums) {
//中午想了半天也不会做啊,感觉就是怎样想办法让每一位都为1,尽可能高位为1,
//用包含最高位的大数和其他数相异或吧,然后找最大的,就是我的思路了
//然后看了标签,字典树?还是不懂
//第一个题解,一个数字有31个二进制位,从高到低确定每一位是否能为1
//那么如何确定每一位是否能为1呢,31一个二进制位逐一判断
//先将每一个数移动k位放入哈希表中,然后对于之前得到的最大数,判断能否与数组中的数异或,得到使当前第k位为1的结果
//这样想我直观想到的一个问题就是最终结果是两个数异或的结果吗
//然后就试着举了个栗子,因为是从高到低,第一位肯定是
int x = 0;
//30位依次遍历
for(int i = 30; i >= 0; i--){
Set<Integer> set = new HashSet<>();
//先把每一个数移动i位的结果放入哈希表中
for(int num : nums){
set.add(num >> i);
}
//使当前位为1
int nextX = x * 2 + 1;
boolean found = false;
//a ^ b = x ==>> a = b ^ x;
for(int num : nums){
if(set.contains(nextX ^ (num >> i))){
found = true;
break;
}
}
if(found){
x = nextX;
}else{
x *= 2;
}
}
return x;
}
}
这样想我直观想到的一个问题就是最终结果是两个数异或的结果吗
然后就试着举了个栗子,因为是从高到低,最高位肯定只有两种情况,0或者1,哈希表中只有两个数,肯定能取1,此时xNext 为1 ,x最终结果为1,此时两个数最高位肯定是0 和 1
第二位可能有四种情况,00,01,10,11, 哈希表中最多有四个数,xNext = 11,而x的最终结果是10 或者11,如果最终结果是11,即此时两个相异或的数,前两位肯定是00和11 或者01和10
再取第三位,同理,8种情况,哈希表中最多8个数,xNext = 111,而x的最终结果是110或者111,如果最终结果这次是110的话,前三位就是在前两位的基础上更进一步,取的数只能是更加精确的两个数
这样处理到最后,就可以得到完整的两个数
字典树
根据上面的哈希表思路想字典树,就好想很多了
从高位到低位创建字典树,每一个结点两个分叉,即是一颗二叉树
在构建后字典树之后,对于数组中的每一个数,都在字典树中找能使得异或结果最大的数
然后再取所有最大数的最大值
具体实现过程中,可以在构建字典树的同时,找当前数与前面数异或的最大值。这样也能保证不漏掉结果
class Trie{
//每个节点两个子节点
Trie[] node = new Trie[2];
}
class Solution {
//根节点
Trie root = new Trie();
public int findMaximumXOR(int[] nums) {
//字典树,试着回想一下写一下
int l = nums.length;
int x = 0;
//i 时,添加i - 1,并计算nums[i]的异或最大值
for(int i = 1; i < l; i++){
add(nums[i - 1]);
x = Math.max(x, check(nums[i]));
}
return x;
}
public void add(int num){
//应该有31层
Trie temp = root;
for(int k = 30; k >= 0; k--){
//取当前位
int cur = (num >> k) & 1;
//如果存在节点,就往下走,不存在就创建
if(temp.node[cur] != null){
temp = temp.node[cur];
}else{
temp.node[cur] = new Trie();
temp = temp.node[cur];
}
}
}
public int check(int num){
Trie temp = root;
int x = 0;
for(int k = 30; k >= 0; k--){
int cur = (num >> k) & 1;
int find = 1 - cur; //cur为1,找0,cur为0找1
if(temp.node[find] != null){
//如果有find,就说明异或结果为1
x = x * 2 + 1;
temp = temp.node[find];
}else{
//否则为0
x = x * 2;
temp = temp.node[cur];
}
}
return x;
}
}
没想到写完,一提交就过了,看来字典树也掌握的还行哈哈
以上是关于LeetCode421. 数组中两个数的最大异或值的主要内容,如果未能解决你的问题,请参考以下文章
leetcode-421-数组中两个数的最大异或值*(前缀树)
LeetCode 421. 数组中两个数的最大异或值 Java 前缀树
LeetCode 421. 数组中两个数的最大异或值 Java 前缀树