LeetCode每日一题——1707. 与数组中元素的最大异或值(字典树)

Posted 小鹏说

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode每日一题——1707. 与数组中元素的最大异或值(字典树)相关的知识,希望对你有一定的参考价值。

前言

本文需要读者了解字典树的相关知识,建议读者先认真看懂这篇博客 字典树(前缀树),在充分理解该题做法后继续阅读。

 

题目描述

给你一个由非负整数组成的数组 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

解题思路

离线询问 + 字典树:即先将字典树构造好。

我们先将每个询问已经它对应的下标用map集合存储起来,因为我们后面需要对询问数组queries按后一个数进行升序排序,目的的方便后面构造字典树,排好序之后,就可以来实现Trie数类,Trie类中主要有两个方法,一个插入insert和一个查询

getValue,然后就可以开始循环扫描一遍询问,此时的询问的数是从小到大排序的,所以前面的询问会先将小的数先构造到数中,后面的询问只需要在这个基础之上继续添加后面的数即可。每当添加完所有小于等于当前询问的那个数后,就开始查询此时树中的所有元素与当前询问的前一个数(queries[i][0])的异或最大值,最后返回res数组即可。

这里需要注意的是:返回的结果要存在对应询问的下标位置。

 

启发

通过这题,我又学会了一个位运算的小技巧:就是给出一个十进制的想要确定它的第i位是0还是1,可以将这个数向右带符号移动 i 位,然后与1做按位与运算就可以确定了。

公式:val >> i & 1

 

AC代码

 1 class Solution {
 2     private Trie root = new Trie();
 3 
 4     public int[] maximizeXor(int[] nums, int[][] queries) {
 5         int n = queries.length;
 6         int[] res = new int[n];
 7 
 8         // 先用map集合将每个询问的下标都存起来
 9         Map<int[], Integer> map = new HashMap<>();
10         for (int i = 0; i < n; i++) {
11             map.put(queries[i], i);
12         }
13 
14         // 排序
15         Arrays.sort(nums);
16         // 按后一个数进行升序排序,这里是为了方便建树
17         Arrays.sort(queries, new Comparator<int[]>() {
18             @Override
19             public int compare(int[] o1, int[] o2) {
20                 return o1[1] - o2[1];
21             }
22         });
23 
24         int index = 0;
25         // 扫描询问
26         for (int i = 0; i < n; i++) {
27             int x = queries[i][0];
28             int m = queries[i][1];
29             // 循环添加,构建trie树
30             while (index < nums.length && nums[index] <= m) {
31                 root.insert(nums[index]);
32                 index++;
33             }
34             // 添加完此次的元素之后,开始查询
35             // 这里的if只要树中添加过一个元素之后,就永远都进不去了
36             if (index == 0) {
37                 // 如果此次没有一个数小于等于m,那就将此时的异或最大值设置为-1
38                 res[map.get(queries[i])] = -1;
39             } else {
40                 res[map.get(queries[i])] = root.getValue(x); 
41             }
42         }
43 
44         return res;
45     }
46 
47 
48 }
49 
50 /*
51 Trie树
52 */
53 class Trie {
54     public static final int L = 31;
55     Trie[] children = new Trie[2];
56 
57     public void insert(int val) {
58         Trie p = this;
59         for (int i = L; i >= 0; i--) {
60             int bit = (val >> i) & 1;
61             if (p.children[bit] == null) {
62                 // 没有孩子,new一个
63                 p.children[bit] = new Trie();
64             }
65             // 如果有,直接指向该孩子
66             p = p.children[bit];
67         }
68     }
69 
70     /***
71      * 返回异或的最大值
72      * @param val
73      * @return
74      */
75     public int getValue(int val) {
76         int ans = 0;
77         Trie p = this;
78         for (int i = L; i >= 0; i--) {
79             // 将第i位移动到第一位(0000000000000000000000000....001,这里的1就是第一位,最前面的0就是第三十二位)
80             int bit = (val >> i) & 1;
81             // 先考虑取反,如果没有,那就只能取同的,即该位最后只能位0(异或:同为0,异为1)
82             if (p.children[bit ^ 1] != null) {
83                 ans += (int) Math.pow(2, i);
84                 // 更新方向
85                 bit ^= 1;
86             }
87             // 向后移动
88             p = p.children[bit];
89         }
90 
91         return ans;
92     }
93 }

leetcode链接

以上是关于LeetCode每日一题——1707. 与数组中元素的最大异或值(字典树)的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode每日一题

《LeetCode之每日一题》:79.数组的度

《LeetCode之每日一题》:52.和为K的子数组

《LeetCode之每日一题》:51.两个数组的交集 II

每日一题系列-leetcode-525-连续数组

《LeetCode之每日一题》:275.两个数组的交集 II