难度 困难
给你一个由非负整数组成的数组 nums 。另有一个查询数组 queries ,其中 queries[i] = [xi, mi] 。
第 i 个查询的答案是 xi 和任何 nums 数组中不超过 mi 的元素按位异或(XOR)得到的最大值。换句话说,答案是 max(nums[j] XOR xi) ,其中所有 j 均满足 nums[j] <= mi 。如果 nums 中的所有元素都大于 mi,最终答案就是 -1 。
返回一个整数数组 answer 作为查询的答案,其中 answer.length == queries.length 且 answer[i] 是第 i 个查询的答案。
示例 1:
输入:nums = [0,1,2,3,4], queries = [[3,1],[1,3],[5,6]]
输出:[3,3,7]
解释:
1) 0 和 1 是仅有的两个不超过 1 的整数。0 XOR 3 = 3 而 1 XOR 3 = 2 。二者中的更大值是 3 。
2) 1 XOR 2 = 3.
3) 5 XOR 2 = 7.
示例 2:输入:nums = [5,2,4,6,6,3], queries = [[12,4],[8,1],[6,3]]
输出:[15,-1,5]
提示:
1 <= nums.length, queries.length <= 105
queries[i].length == 2
0 <= nums[j], xi, mi <= 109来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-xor-with-an-element-from-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
第220期周赛中科普了一下离线算法,没想到这一周就用到了,关于离线算法可以看下这个链接:https://blog.csdn.net/lang_a/article/details/81204180
这道题的思路和和 421. 数组中两个数的最大异或值 一致,多了一步离线排序的过程
421题的思路如下:
思路分析:这种题就不要暴力法,指名道姓说要O(n)。根据提示需要使用建树。
首先我们需要知道,二进制高位为1会大于低位的所有和,比如"11111111"最高位代表的"1"按权展开为128,
而后面的“1111111”按权展开的和也只是127。所以进行异或时应该尽量选择高位异或结果为“1”的。
第一步:遍历数组,我们按照二进制[31,30,…,1, 0]各位的状态进行建树,left放置0,right放置1。
比如某个int型数的二进制是"0110110…",我们需要将其放置到[left,right,right,left,right,right,left…]。
第二步:遍历数组,按照贪心策略,尽量维持当前选择的方向能保证当前能位异或结果为1。
---------------------
作者:hestyle
来源:CSDN
原文:https://blog.csdn.net/qq_41855420/article/details/88756998
版权声明:本文为博主原创文章,转载请附上博文链接!
有了上面的分析很容易就可以想到这题是对queirs针对limit进行排序,然后对nums排序,遍历排序后的queirs取出nums中小于当前limit的数字建立字典树,贪心的方式进行每一次选择即可得到所有的查询结果。
JAVA实现如下:
421题:
class Solution {
class Node {
public Node left;
public Node right;
public Node() {
}
}
public int findMaximumXOR(int[] nums) {
// 二叉树模拟 字典树,左为1,右为0
// 树初始化
Node root = new Node();
// 小于2^30时 使用30位作为掩码
int mask = 1 << 31;
for (int i : nums) {
String s = Integer.toBinaryString(mask | i);
Node cur = root;
// 30位分解
for (char c : s.toCharArray()) {
if (c == '0') {
if (cur.right == null) {
cur.right = new Node();
}
cur = cur.right;
} else {
if (cur.left == null) {
cur.left = new Node();
}
cur = cur.left;
}
}
}
int max = Integer.MIN_VALUE;
// 遍历树,贪心规则 每遍历一位时尽量XOR为1
for (int i : nums) {
String s = Integer.toBinaryString(mask | i);
Node cur = root;
StringBuilder xor = new StringBuilder();
for (char c : s.toCharArray()) {
if (c == '0') {
if (cur.left != null) {
cur = cur.left;
xor.append("1");
}else{
cur = cur.right;
xor.append("0");
}
} else {
if (cur.right != null) {
cur = cur.right;
xor.append("1");
}else{
cur = cur.left;
xor.append("0");
}
}
}
int tem = Integer.parseInt(xor.toString(),2);
max = Math.max(max,tem);
}
return max;
}
}
1707题:
class Solution {
class Node {
public Node left;
public Node right;
public Node() {
}
}
public int[] maximizeXor(int[] nums, int[][] queries) {
// 存放结果
Map<Query, Integer> hm = new HashMap<>();
// 离线排序
Query[] queries1 = new Query[queries.length];
for (int i = 0; i < queries.length; i++) {
queries1[i] = new Query(queries[i][0], queries[i][1]);
}
Arrays.sort(queries1, new Comparator<Query>() {
@Override
public int compare(Query o1, Query o2) {
return o1.limit - o2.limit;
}
});
// 初始化字典树,并贪心求值
Arrays.sort(nums);
// 树初始化
Node root = new Node();
// 小于10^9 使用31位作为掩码
int mask = 1 << 31;
int index = 0;
for (int i : nums) {
while (index<queries.length&&i > queries1[index].limit) {
// 遍历树,贪心规则 每遍历一位时尽量XOR为1
int tem = getMaxXorVal(root, mask, queries1[index].val);
hm.put(queries1[index], tem);
index++;
}
String s = Integer.toBinaryString(mask | i);
Node cur = root;
// 30位分解
for (char c : s.toCharArray()) {
if (c == '0') {
if (cur.right == null) {
cur.right = new Node();
}
cur = cur.right;
} else {
if (cur.left == null) {
cur.left = new Node();
}
cur = cur.left;
}
}
}
int[] answer = new int[queries.length];
for (int i = 0; i < queries.length; i++) {
if (hm.containsKey(new Query(queries[i][0], queries[i][1]))) {
answer[i] = hm.get(new Query(queries[i][0], queries[i][1]));
} else {
answer[i] = getMaxXorVal(root, mask, queries[i][0]);
}
}
return answer;
}
private int getMaxXorVal(Node root, int mask, int val) {
String s = Integer.toBinaryString(mask | val);
Node cur = root;
if (root.left == null && root.right == null) {
return -1;
}
StringBuilder xor = new StringBuilder();
for (char c : s.toCharArray()) {
if (c == '0') {
if (cur.left != null) {
cur = cur.left;
xor.append("1");
} else {
cur = cur.right;
xor.append("0");
}
} else {
if (cur.right != null) {
cur = cur.right;
xor.append("1");
} else {
cur = cur.left;
xor.append("0");
}
}
}
return Integer.parseInt(xor.toString(), 2);
}
class Query {
int val;
int limit;
public Query(int val, int limit) {
this.val = val;
this.limit = limit;
}
@Override
public int hashCode() {
return val + limit;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Query) {
Query obj2 = (Query) obj;
if (obj2.limit == this.limit && obj2.val == this.val) {
return true;
}
}
return false;
}
}
}
执行结果:
421:
1707:
注:时间复杂度上满足要求,但实现上仍有可剪枝和降低系数的方式,如421题每个数字求异或值时可以判断是否已经比max小,如果是则可以剪枝。