Cycle Length Queries in a Tree - Complete Solution Guide
Cycle Length Queries in a Tree is LeetCode problem 2509, a Hard level challenge. This complete guide provides step-by-step explanations, multiple solution approaches, and optimized code in python3, java, cpp, c.
Problem Statement
You are given an integer n . There is a complete binary tree with 2 n - 1 nodes. The root of that tree is the node with the value 1 , and every node with a value val in the range [1, 2 n - 1 - 1] has two children where: The left node has the value 2 * val , and The right node has the value 2 * val + 1 . You are also given a 2D integer array queries of length m , where queries[i] = [a i , b i ] . For each query, solve the following problem: Add an edge between the nodes with values a i and b i .
Detailed Explanation
The problem describes a complete binary tree where nodes are numbered in a specific way: the root is 1, and a node with value 'val' has children 2*val and 2*val + 1. We're given a list of queries, each containing two node values (a, b). For each query, we need to imagine adding an edge between nodes 'a' and 'b', find the length of the resulting cycle, and then return that length. Importantly, we *don't* actually modify the tree; we only need to calculate the cycle length that *would* result. The key is understanding how node numbering relates to the tree structure and finding the shortest path between two nodes by navigating towards their common ancestor.
Solution Approach
The solution finds the cycle length for each query by calculating the combined path length from the two nodes in the query to their lowest common ancestor (LCA). It iteratively divides the larger of the two node values by 2, which simulates moving up the tree towards the root. This process continues until the two node values are equal, at which point the lowest common ancestor is found. The total number of steps taken during this process, plus 1, is the length of the cycle.
Step-by-Step Algorithm
- Step 1: Initialize an empty list 'answer' to store the cycle lengths for each query.
- Step 2: Iterate through each query (a, b) in the 'queries' list.
- Step 3: Initialize 'path_len' to 0. This variable will store the combined length of the paths from 'a' and 'b' to their lowest common ancestor.
- Step 4: Use a 'while' loop to continue as long as 'a' is not equal to 'b'.
- Step 5: Inside the loop, compare 'a' and 'b'. If 'a' is greater than 'b', divide 'a' by 2 (integer division) and increment 'path_len'. This moves 'a' one level up the tree.
- Step 6: If 'b' is greater than 'a', divide 'b' by 2 (integer division) and increment 'path_len'. This moves 'b' one level up the tree.
- Step 7: Once the 'while' loop finishes (when 'a' equals 'b'), it means the lowest common ancestor has been found. The total path length is 'path_len'.
- Step 8: Add 1 to 'path_len' to account for the direct edge between the original 'a' and 'b' nodes that completes the cycle. Append this sum to the 'answer' list.
- Step 9: After processing all queries, return the 'answer' list.
Key Insights
- Insight 1: The cycle is formed by the path from node 'a' to their lowest common ancestor (LCA), the path from node 'b' to the LCA, and the edge between 'a' and 'b'. The cycle length is the sum of these path lengths + 1.
- Insight 2: The problem can be solved by repeatedly dividing the larger of the two node values by 2 until they become equal. Each division represents moving one step up towards the root of the tree.
- Insight 3: The number of steps taken to reach the common ancestor, plus 1 (for the edge between the two nodes in the query), gives the cycle length.
Complexity Analysis
Time Complexity: O(q log n)
Space Complexity: O(q)
Topics
This problem involves: Array, Tree, Binary Tree.
Companies
Asked at: Arcesium.