【Leetcode】2509. 查询树中环的长度——1948

📝 题目

2509. 查询树中环的长度

给你一个整数 n ,表示你有一棵含有 2n - 1 个节点的 完全二叉树 。根节点的编号是 1 ,树中编号在[1, 2n - 1 - 1] 之间,编号为 val 的节点都有两个子节点,满足:

  • 左子节点的编号为 2 * val
  • 右子节点的编号为 2 * val + 1

给你一个长度为 m 的查询数组 queries ,它是一个二维整数数组,其中 queries[i] = [ai, bi] 。对于每个查询,求出以下问题的解:

  • 在节点编号为 aibi 之间添加一条边。
  • 求出图中环的长度。
  • 删除节点编号为 aibi 之间新添加的边。

注意:

  • 是开始和结束于同一节点的一条路径,路径中每条边都只会被访问一次。
  • 环的长度是环中边的数目。
  • 在树中添加额外的边后,两个点之间可能会有多条边。

请你返回一个长度为 m 的数组 answer ,其中 answer[i] 是第 i 个查询的结果。

提示:

  • 2 <= n &lt;= 30
  • m == queries.length
  • 1 &lt;= m &lt;= 105
  • queries[i].length == 2
  • 1 &lt;= ai, bi &lt;= 2n - 1
  • ai != bi

🔍 思路

首先可以明确的是,在本题目中,每个查询之间都是独互不影响的。

而对于每个查询而言,所求得的环的长度,也实际上就是找到其公共最近祖先之后连接而成的环。

如果采用常规的寻找最近的公共祖先,那么每次的查询的时间复杂度就是需要遍历完所有的节点,在本题目中就是\(O(2^{n})\),很显眼是个糟糕的算法

此时我们回到题目中,发现实际上对于题目关于完全二叉树这个性质以及节点和其子节点之间的关系这个性质没有用到。

如果我们从节点本身出发,当前层到下一层就是右移一位。那么公共的祖先就是其从高位开始最长的连续相等二进制位的长度

由于查询的两个节点不一定在同一层,因此每次查询的时候其节点对应的二进制长度可能会不同,为了找到上述最长的连续相等二进制位,我们首先对较长二进制数字也就是深度较深的节点往上走,找到其父节点,即左移一位。直至两个节点回到同一深度。并记录左移的次数。

此时得到的两个深度相等的节点,两者同时向上移动,直到到达同一个节点即找到了公共最近祖先。

当然对于左移的次数,实际上也是两个节点最高位不相等二进制所在的位置。即
(l ^r).bit_length(),由于两个节点都是需要移动的,再乘以2即可。

✍️ 代码实现

class Solution:
    def cycleLengthQueries(self, n: int, queries: List[List[int]]) -> List[int]:
        ans = [0]*len(queries)
        for i,(x,y) in enumerate(queries):
            # 左大右小
            l,r = (x,y) if x>y else (y,x)
            # 较长二进制位置 需要左移的次数
            temp = l.bit_length() - r.bit_length()
            ans[i] = (temp+((l>>temp)^r).bit_length()*2+1)
        return ans
posted @ 2024-12-24 21:02  TICSMC  阅读(10)  评论(0)    收藏  举报