Talentd
FleetCode
Make and follow sheets.Sign in

Cycle Length Queries in a Tree solution and explanation

HardArrayBinary TreeTree7 min read60.9% acceptanceUpdated 9 Apr 2026
Weekly Contest 324 | Cycle Length Queries in a TreecodingMohan · 18:31 · 764 viewsWatch on YouTube
9 more videos on this problem
  1. Weekly Contest 324 | Cycle Length Queries in a TreecodingMohan · 18:31 · 764 views
  2. 2509. Cycle Length Queries in a Tree | Weekly Contest 324 | LeetCode 2509Bro Coders · 11:50 · 646 views
  3. 2509 Cycle Length Queries in a Tree | Leetcode Weekly contest 324Programming Pathshala · 19:20 · 479 views
  4. 【每日一题】LeetCode 2509. Cycle Length Queries in a TreeHuifeng Guan · 6:31 · 253 views
  5. Leetcode Weekly contest 324 - Hard - Cycle Length Queries in a TreePrakhar Agrawal · 12:28 · 251 views
  6. 2509. Cycle Length Queries in a Tree (Leetcode Hard)Programming Live with Larry · 12:02 · 193 views
  7. Cycle Length Queries in a Tree | 2509 LeetCode | Graphs | Leetcode Weekly Contest 324CodeWithSunny · 19:53 · 119 views
  8. Cycle Length Queries in a Tree - PythonCheatCode Ninja · 8:46 · 81 views
  9. Leetcode 2509 Cycle Length Queries in a Tree (Java) - Leetcode Contest 324TheAnalyticGuy · 10:43 · 74 views
  10. LC 2509. Cycle Length Queries in a Tree (Explaination+Code) | Leetcode weekly contest 324Code Bean · 6:54 · 62 views

The problem

You are given an integer n. There is a complete binary tree with 2n - 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, 2n - 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] = [ai, bi]. For each query, solve the following problem:

  1. Add an edge between the nodes with values ai and bi.
  2. Find the length of the cycle in the graph.
  3. Remove the added edge between nodes with values ai and bi.

Note that:

  • A cycle is a path that starts and ends at the same node, and each edge in the path is visited only once.
  • The length of a cycle is the number of edges visited in the cycle.
  • There could be multiple edges between two nodes in the tree after adding the edge of the query.

Return an array answer of length m where answer[i] is the answer to the ith query.

Example 1:

Input: n = 3, queries = [[5,3],[4,7],[2,3]]
Output: [4,5,3]
Explanation: The diagrams above show the tree of 23 - 1 nodes. Nodes colored in red describe the nodes in the cycle after adding the edge.
- After adding the edge between nodes 3 and 5, the graph contains a cycle of nodes [5,2,1,3]. Thus answer to the first query is 4. We delete the added edge and process the next query.
- After adding the edge between nodes 4 and 7, the graph contains a cycle of nodes [4,2,1,3,7]. Thus answer to the second query is 5. We delete the added edge and process the next query.
- After adding the edge between nodes 2 and 3, the graph contains a cycle of nodes [2,1,3]. Thus answer to the third query is 3. We delete the added edge.

Example 2:

Input: n = 2, queries = [[1,2]]
Output: [2]
Explanation: The diagram above shows the tree of 22 - 1 nodes. Nodes colored in red describe the nodes in the cycle after adding the edge.
- After adding the edge between nodes 1 and 2, the graph contains a cycle of nodes [2,1]. Thus answer for the first query is 2. We delete the added edge.

Constraints:

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

Approach overview

Problem Overview: You are given queries on an infinite complete binary tree where node i has parent i/2. For each query [a, b], an extra edge is temporarily added between the two nodes. That edge forms exactly one cycle. The task is to compute the length of that cycle for every query.

Approach 1: Using an Array (Path to Root Comparison) (Time: O(log n) per query, Space: O(log n))

This approach builds the path from each node to the root and stores those ancestors in arrays. Starting from a and b, repeatedly divide by 2 to move up the tree until reaching the root. Once both ancestor paths are built, scan from the root side to locate the lowest common ancestor (LCA). The distance from a to the LCA plus the distance from b to the LCA gives the number of tree edges between the nodes. Adding the new edge forms a cycle, so the final cycle length becomes distance + 1. Each node moves up at most log₂(n) levels because the structure behaves like a heap-style binary tree. This method is straightforward and easy to reason about since the ancestor lists make the LCA detection explicit.

Approach 2: Using a Linked List / Pointer Walk (Upward Traversal) (Time: O(log n) per query, Space: O(1))

This approach avoids storing ancestor paths. Instead, simulate climbing toward the root from both nodes simultaneously. While a != b, always move the larger node upward by replacing it with a/2 or b/2. Because node labels increase as you go down the tree, the larger label must be deeper. Each upward step reduces the depth difference until both pointers meet at their LCA. Count the number of steps taken during this process. The cycle length equals the step count plus one for the added edge. The method relies on the heap-like numbering of this tree structure and performs only simple integer operations. No auxiliary data structures are needed beyond a counter.

The key observation: the cycle formed after adding the edge equals distance(a, b) + 1. Computing the distance in this implicit tree reduces to repeatedly moving nodes toward their lowest common ancestor. Because the height of the tree grows logarithmically, each query runs in O(log n) time.

Recommended for interviews: The upward traversal method is the expected solution. It demonstrates understanding of implicit tree structures and LCA reasoning without building the tree explicitly. The array-based approach still shows solid reasoning about ancestor paths, but the pointer-walk solution is cleaner and uses constant space. Both rely on properties of a heap-indexed array-like representation of a binary tree.

Approach 1: Approach 1: Using an Array

This approach focuses on solving the problem using a simple array. We iterate through the array to achieve the desired result based on the problem requirements.

def solve(arr):
    for i in arr:
        # Perform operations on i.
        print(i, end=' ')

arr = [1, 2, 3, 4, 5]
solve(arr)
public class Main {
    public static void solve(int arr[]) {
        for (int i : arr) {
            // Perform operations on each element.
            System.out.print(i + " ");
        }
    }
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        solve(arr);
    }
}
#include <iostream>
using namespace std;
void solve(int arr[], int n) {
    for(int i = 0; i < n; i++) {
        // Perform operations on arr[i].
        cout << arr[i] << " ";
    }
}
int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int n = sizeof(arr)/sizeof(arr[0]);
    solve(arr, n);
    return 0;
}
function solve(arr) {
    arr.forEach(i => {
        // Perform operations on i.
        process.stdout.write(i + ' ');
    });
}

const arr = [1, 2, 3, 4, 5];
solve(arr);
#include <stdio.h>
void solve(int arr[], int n) {
    for(int i = 0; i < n; i++) {
        // Perform operations on arr[i].
        printf("%d ", arr[i]);
    }
}
int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int n = sizeof(arr)/sizeof(arr[0]);
    solve(arr, n);
    return 0;
}
using System;
class Program {
    static void Solve(int[] arr) {
        foreach (int i in arr) {
            // Perform operations on i.
            Console.Write(i + " ");
        }
    }
    public static void Main() {
        int[] arr = {1, 2, 3, 4, 5};
        Solve(arr);
    }
}

The Python solution uses a for loop to iterate over each element in the list and print it.

Complexity

Time Complexity: O(n)
Space Complexity: O(1)

Try this approach in the editor

Approach 2: Approach 2: Using a Linked List

This approach utilizes a linked list to manage elements dynamically. We traverse the linked list and perform operations analogous to those required in a more static array setting.

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

def print_list(head):
    current = head
    while current:
        print(current.data, end=' ')
        current = current.next

head = Node(1)
head.next = Node(2)
head.next.next = Node(3)
print_list(head)
class Node {
    int data;
    Node next;
    Node(int d) { data = d; next = null; }
}
public class Main {
    public static void printList(Node head) {
        Node current = head;
        while (current != null) {
            System.out.print(current.data + " ");
            current = current.next;
        }
    }
    public static void main(String[] args) {
        Node head = new Node(1);
        head.next = new Node(2);
        head.next.next = new Node(3);
        printList(head);
    }
}
#include <iostream>
using namespace std;
struct Node {
    int data;
    Node* next;
    Node(int val) : data(val), next(NULL) {}
};
void printList(Node* n) {
    while (n != NULL) {
        cout << n->data << " ";
        n = n->next;
    }
}
int main() {
    Node* head = new Node(1);
    head->next = new Node(2);
    head->next->next = new Node(3);
    printList(head);
    return 0;
}
class Node {
    constructor(data) {
        this.data = data;
        this.next = null;
    }
}
function printList(head) {
    let current = head;
    while (current !== null) {
        process.stdout.write(current.data + ' ');
        current = current.next;
    }
}

let head = new Node(1);
head.next = new Node(2);
head.next.next = new Node(3);
printList(head);
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
    int data;
    struct Node* next;
} Node;
void printList(Node* n) {
    while (n != NULL) {
        printf("%d ", n->data);
        n = n->next;
    }
}
int main() {
    Node* head = NULL;
    Node* second = NULL;
    Node* third = NULL;
    head = (Node*)malloc(sizeof(Node));
    second = (Node*)malloc(sizeof(Node));
    third = (Node*)malloc(sizeof(Node));
    head->data = 1;
    head->next = second;
    second->data = 2;
    second->next = third;
    third->data = 3;
    third->next = NULL;
    printList(head);
    return 0;
}
using System;
class Node {
    public int data;
    public Node next;
    public Node(int d) { data = d; next = null; }
}
class Program {
    static void PrintList(Node head) {
        Node current = head;
        while (current != null) {
            Console.Write(current.data + " ");
            current = current.next;
        }
    }
    static void Main() {
        Node head = new Node(1);
        head.next = new Node(2);
        head.next.next = new Node(3);
        PrintList(head);
    }
}

Python's object-oriented approach is suited to creating nodes and linking them. The list is then traversed using a while loop, printing each node's data.

Complexity

Time Complexity: O(n)
Space Complexity: O(n) for linked list nodes

Try this approach in the editor

Approach 3: Finding the Lowest Common Ancestor

For each query, we find the lowest common ancestor of the two nodes a and b, and record the number of steps taken upwards. The answer to the query is the number of steps plus one.

To find the lowest common ancestor, if a > b, we move a to its parent node; if a < b, we move b to its parent node. We accumulate the number of steps until a = b.

The time complexity is O(n times m), where m is the length of the queries array.

func cycleLengthQueries(n int, queries [][]int) []int {
	ans := []int{}
	for _, q := range queries {
		a, b := q[0], q[1]
		t := 1
		for a != b {
			if a > b {
				a >>= 1
			} else {
				b >>= 1
			}
			t++
		}
		ans = append(ans, t)
	}
	return ans
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Approach 1: Using an Array

Time Complexity: O(n)
Space Complexity: O(1)

Approach 2: Using a Linked List

Time Complexity: O(n)
Space Complexity: O(n) for linked list nodes

Finding the Lowest Common Ancestor–

Complexity analysis

ApproachTimeSpaceWhen to Use
Array (Store Path to Root)O(log n) per queryO(log n)Useful when you want explicit ancestor paths and clearer LCA reasoning
Linked List / Pointer Upward TraversalO(log n) per queryO(1)Preferred solution for interviews due to constant space and simple implementation

Frequently asked questions

Is Cycle Length Queries in a Tree easy or hard?

The problem is labeled Hard because it requires recognizing the implicit tree structure and reducing the task to a lowest common ancestor distance problem. Once that insight is clear, the implementation becomes short and efficient with O(log n) time per query.

Cycle Length Queries in a Tree Python/Java solution

Python and Java implementations typically use a loop that moves the larger node upward until both nodes become equal. Each iteration divides the deeper node by two and increments a counter. After the loop finishes, add one to the counter to represent the extra edge that creates the cycle.

How to solve Cycle Length Queries in a Tree in O(n)?

The typical optimized solution processes each query independently in O(log n) time using upward traversal toward the LCA. During each step, divide the larger node by 2 until both nodes match. Count the steps and add one to represent the newly added edge forming the cycle. For q queries, the full runtime is O(q log n).

What is the best approach for Cycle Length Queries in a Tree?

The most efficient approach is upward traversal to the lowest common ancestor (LCA). For each query, repeatedly move the larger node upward by dividing it by 2 until both nodes meet. The number of steps taken gives the distance between nodes, and the cycle length is distance + 1. This runs in O(log n) time per query and O(1) space.

Is Cycle Length Queries in a Tree asked at Google/Amazon/Meta?

This type of problem reflects common interview themes such as lowest common ancestor, binary tree traversal, and heap-style indexing. Variations of LCA and implicit tree navigation frequently appear in interviews at companies like Google, Amazon, and Meta. The problem tests reasoning about tree structure without explicitly building the tree.

What data structure is used in Cycle Length Queries in a Tree?

The problem relies on properties of a complete binary tree where node indices behave like a heap stored in an array. The optimal approach does not require building an explicit tree structure. Instead, simple integer operations simulate moving to a parent node using division by two.

What is the time complexity of Cycle Length Queries in a Tree?

Each query requires moving nodes up the binary tree toward their lowest common ancestor. Since the height of a complete binary tree is about log2(n), the traversal takes O(log n) time. With q queries, the total complexity becomes O(q log n). Space complexity can be O(1) with the pointer traversal approach.

Comments

Now write it yourselfClose this page and solve Cycle Length Queries in a Tree in the editor, without looking.
Practise Cycle Length Queries in a Tree