开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第23天,点击查看活动详情
力扣——6268. 查询树中环的长度
6268. 查询树中环的长度
难度困难1收藏分享切换为英文接收动态反馈
给你一个整数 n ,表示你有一棵含有 2n - 1 个节点的 完全二叉树 。根节点的编号是 1 ,树中编号在[1, 2n - 1 - 1] 之间,编号为 val 的节点都有两个子节点,满足:
- 左子节点的编号为
2 * val - 右子节点的编号为
2 * val + 1
给你一个长度为 m 的查询数组 queries ,它是一个二维整数数组,其中 queries[i] = [ai, bi] 。对于每个查询,求出以下问题的解:
- 在节点编号为
ai和bi之间添加一条边。 - 求出图中环的长度。
- 删除节点编号为
ai和bi之间新添加的边。
注意:
- 环 是开始和结束于同一节点的一条路径,路径中每条边都只会被访问一次。
- 环的长度是环中边的数目。
- 在树中添加额外的边后,两个点之间可能会有多条边。
请你返回一个长度为 m 的数组 answer ,其中 answer[i] 是第 i 个查询的结果 。
示例 1:
输入:n = 3, queries = [[5,3],[4,7],[2,3]]
输出:[4,5,3]
解释:上图是一棵有 23 - 1 个节点的树。红色节点表示添加额外边后形成环的节点。
- 在节点 3 和节点 5 之间添加边后,环为 [5,2,1,3] ,所以第一个查询的结果是 4 。删掉添加的边后处理下一个查询。
- 在节点 4 和节点 7 之间添加边后,环为 [4,2,1,3,7] ,所以第二个查询的结果是 5 。删掉添加的边后处理下一个查询。
- 在节点 2 和节点 3 之间添加边后,环为 [2,1,3] ,所以第三个查询的结果是 3 。删掉添加的边。
示例 2:
输入:n = 2, queries = [[1,2]]
输出:[2]
解释:上图是一棵有 22 - 1 个节点的树。红色节点表示添加额外边后形成环的节点。
- 在节点 1 和节点 2 之间添加边后,环为 [2,1] ,所以第一个查询的结果是 2 。删掉添加的边。
提示:
2 <= n <= 30m == queries.length1 <= m <= 105queries[i].length == 21 <= ai, bi <= 2n - 1ai != bi
问题解析
首先很明显的1个突破点:
- 设a点和b点的最近公共祖先为x。那么环的长度应该是:a到x的距离+b到x的距离+1(因为a和b之间多了一个路)
那么这一题的主要问题就是找a和b的最近公共组先了。
注意到,树是一个完全二叉树,树中编号在[1, 2n - 1 - 1] 之间。
在完全二叉树中,点i的左孩子节点是2*i,右孩子节点是2 *i+1。
我们可以反过来,通过将当前点的值变为一半得到这个点的父结点。
我们可以通过将a和b两个点一点点往上挪动,它俩重复走过的第一个点就是它们的最近公共组先。
我的做法:
- 对于点a,只要a不为1,每一次就把a变为原来的一半,同时用哈希表mymap记录下a点到达当前点的步骤。
- 对于点b,只要b不为1,每一次就把a变为原来的一半,当当前点出现在mymap里时,说明找到最近公共组先。
- 答案就为:a到组先的距离+b到组先的距离+1
AC代码
class Solution {
public:
vector<int> cycleLengthQueries(int n, vector<vector<int>>& queries) {
vector<int>ans;
for(auto&i:queries)
{
int a=i[0],b=i[1],cnt=0;
unordered_map<int,int>mymap;
mymap[a]=0;
while(a!=1)
{
cnt++;
a/=2;
mymap[a]=cnt;
}
cnt=0;
while(!mymap.count(b))
{
cnt++;
b/=2;
}
ans.push_back(cnt+mymap[b]+1);
}
return ans;
}
};