LeetCode——1938. 查询最大基因差[Maximum Genetic Difference Query][困难]——分析及代码[Java]
- 一、题目
- 二、分析及代码
- 1. 离线查询 + 字典树
- (1)思路
- (2)代码
- (3)结果
- 三、其他
一、题目
给你一棵 n 个节点的有根树,节点编号从 0 到 n - 1 。每个节点的编号表示这个节点的 独一无二的基因值 (也就是说节点 x 的基因值为 x)。两个基因值的 基因差 是两者的 异或和 。给你整数数组 parents ,其中 parents[i] 是节点 i 的父节点。如果节点 x 是树的 根 ,那么 parents[x] == -1 。
给你查询数组 queries ,其中 queries[i] = [nodei, vali] 。对于查询 i ,请你找到 vali 和 pi 的 最大基因差 ,其中 pi 是节点 nodei 到根之间的任意节点(包含 nodei 和根节点)。更正式的,你想要最大化 vali XOR pi 。
请你返回数组 ans ,其中 ans[i] 是第 i 个查询的答案。
示例 1:
输入:parents = [-1,0,1,1], queries = [[0,2],[3,2],[2,5]]
输出:[2,3,7]
解释:查询数组处理如下:
- [0,2]:最大基因差的对应节点为 0 ,基因差为 2 XOR 0 = 2 。
- [3,2]:最大基因差的对应节点为 1 ,基因差为 2 XOR 1 = 3 。
- [2,5]:最大基因差的对应节点为 2 ,基因差为 5 XOR 2 = 7 。
示例 2:
输入:parents = [3,7,-1,2,0,7,0,2], queries = [[4,6],[1,15],[0,5]]
输出:[6,14,7]
解释:查询数组处理如下:
- [4,6]:最大基因差的对应节点为 0 ,基因差为 6 XOR 0 = 6 。
- [1,15]:最大基因差的对应节点为 1 ,基因差为 15 XOR 1 = 14 。
- [0,5]:最大基因差的对应节点为 2 ,基因差为 5 XOR 2 = 7 。
提示:
- 2 <= parents.length <= 10^5
- 对于每个 不是 根节点的 i ,有 0 <= parents[i] <= parents.length - 1 。
- parents[root] == -1
- 1 <= queries.length <= 3 * 10^4
- 0 <= nodei <= parents.length - 1
- 0 <= vali <= 2 * 10^5
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-genetic-difference-query
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二、分析及代码
1. 离线查询 + 字典树
(1)思路
根据题意,对于每个查询,需要求解从该位置到根之间所有节点与对应数字的最大异或值。而深度优先搜索过程中,当前入队的部分正是该节点及其所有层级的父节点,因此可结合 DFS 方法进行离线搜索。
对最大异或值的计算,可结合字典树方法进行。
本题需涉及对字典树中数值的删除操作,为简化代码,可在字典树的节点中设计一个计数器,记录当前该节点对应的数字个数,从而避免删除实际节点。
(2)代码
class Solution {
Trie trie;//字典树根节点
Map<Integer, List<Integer>> tree;//树中各个节点对应的子节点
Map<Integer, List<Integer>> queVal;//树中各个节点对应的查询值
Map<Integer, List<Integer>> queId;//树中各个节点对应的queries下标
int[] ans;//输出答案
public int[] maxGeneticDifference(int[] parents, int[][] queries) {
int n = parents.length, m = queries.length, root = -1;
//记录树中各个节点对应的子节点
this.tree = new HashMap<>();
for (int i = 0; i < n; i++) {
if (parents[i] == -1)
root = i;
else {
if (tree.get(parents[i]) == null)
tree.put(parents[i], new ArrayList<Integer>());
tree.get(parents[i]).add(i);
}
}
//记录树中各个节点对应的查询值和下标
this.queVal = new HashMap<>();
this.queId = new HashMap<>();
for (int i = 0; i < m; i++) {
int node = queries[i][0], val = queries[i][1];
if (queVal.get(node) == null) {
queVal.put(node, new ArrayList<Integer>());
queId.put(node, new ArrayList<Integer>());
}
queVal.get(node).add(val);
queId.get(node).add(i);
}
//深度优先搜索
this.ans = new int[m];
this.trie = new Trie();
dfs(root);
return ans;
}
//深度优先搜索
public void dfs(int node) {
trie.insert(node);//当前节点加入字典树
if (queVal.containsKey(node)) {//处理针对当前节点的查询
for (int i = 0; i < queVal.get(node).size(); i++) {
ans[queId.get(node).get(i)] = trie.getMaxXor(queVal.get(node).get(i));//在字典树中查询
}
}
if (tree.containsKey(node)) {//当前节点存在子节点
for (int i : tree.get(node))
dfs(i);//深度优先搜索
}
trie.del(node);//从字典树中删除当前节点
return;
}
}
//字典树
class Trie {
static final int H = 18;//树高度,本题val<=2*10^5<2^18
Trie[] child = new Trie[2];//0、1子节点
int cnt = 0;//当前节点对应的数值个数,简化删除操作
//插入数值
public void insert(int val) {
Trie node = this;
for (int i = H - 1; i >= 0; i--) {
int bit = (val >> i) & 1;
if (node.child[bit] == null)
node.child[bit] = new Trie();
node = node.child[bit];
node.cnt++;
}
return;
}
//删除数值
public void del(int val) {
Trie node = this;
for (int i = H - 1; i >= 0; i--) {
int bit = (val >> i) & 1;
node = node.child[bit];
node.cnt--;
}
return;
}
//针对数值查询当前字典树对应的最大异或值
public int getMaxXor(int val) {
Trie node = this;
int ret = 0;
for (int i = H - 1; i >= 0; i--) {
int bit = (val >> i) & 1 ^ 1;
if (node.child[bit] != null && node.child[bit].cnt != 0) {
ret += (1 << i);
node = node.child[bit];
} else
node = node.child[1 ^ bit];
}
return ret;
}
}
(3)结果
执行用时 :251 ms,在所有 Java 提交中击败了 47.48% 的用户;
内存消耗 :294.4 MB,在所有 Java 提交中击败了 43.01% 的用户。
三、其他
暂无。