Path In Zigzag Labelled Binary Tree

Med
#1045Time: O(label) - To find the path for a given `label`, we must construct the tree up to the level of that label. The number of nodes in the tree grows exponentially with the level. The total number of nodes processed is proportional to the value of `label` itself.Space: O(label) - The space required is dominated by the `parentMap` and the queue used for level-order traversal. In the worst case, we might store information for all nodes up to the level of the target `label`. The number of nodes up to level `d` is `2^(d+1) - 1`. Since `d` is `log2(label)`, the number of nodes is approximately `2*label`.
Patterns
Data structures

Prompt

In an infinite binary tree where every node has two children, the nodes are labelled in row order.

In the odd numbered rows (ie., the first, third, fifth,...), the labelling is left to right, while in the even numbered rows (second, fourth, sixth,...), the labelling is right to left.

Given the label of a node in this tree, return the labels in the path from the root of the tree to the node with that label.

 

Example 1:

Input: label = 14
Output: [1,3,4,14]

Example 2:

Input: label = 26
Output: [1,2,6,10,26]

 

Constraints:

  • 1 <= label <= 10^6

Approaches

2 approaches with complexity analysis and trade-offs.

This approach involves a direct simulation of the tree's construction. We build the tree level by level up to the level containing the target label. While building, we maintain a map of each node's label to its parent's label. After the necessary portion of the tree is constructed and the parent relationships are mapped, we can easily backtrack from the target label to the root to find the required path.

Algorithm

  • Determine the level of the target label. Let this be max_level.
  • Create a map to store parent-child relationships, for instance, Map<Integer, Integer> parentMap.
  • Simulate the tree construction level by level from the root (level 0) up to max_level.
  • For each level, calculate the range of labels and generate them according to the zigzag pattern (left-to-right for odd rows, right-to-left for even rows).
  • As each level's nodes are generated, populate the parentMap by linking them to their parents from the previous level.
  • Once the map is built, trace the path from the target label back to the root by repeatedly querying the parentMap.
  • Collect the labels in a list and reverse it to obtain the final path from root to target.

Walkthrough

The algorithm proceeds as follows:

  1. First, we determine the depth of the tree required to include the given label. The level d of a node with a given label can be found by d = floor(log2(label)).
  2. We then construct the tree level by level. We use a list to hold the labels of the nodes at the current level and a map to store child -> parent mappings.
  3. We start with level 0, which contains only the root node with label 1.
  4. We then iterate from level 1 up to the target level. In each iteration, we generate the labels for the new level. The labels are generated from left-to-right (2^d to 2^(d+1)-1) if the row number is odd (i.e., level d is even), and right-to-left if the row number is even (i.e., level d is odd).
  5. For each parent in the previous level, we associate its two children from the newly generated list of labels and update our parent map.
  6. After the simulation is complete, the parentMap contains all the necessary links to trace the path. We start from the target label, add it to our result path, and then find its parent from the map. We repeat this process until we reach the root (label 1).
  7. Finally, the collected path is reversed to get the correct order from root to the target node.
import java.util.*; class Solution {    public List<Integer> pathInZigZagTree(int label) {        if (label == 1) {            return Collections.singletonList(1);        }         Map<Integer, Integer> parentMap = new HashMap<>();        parentMap.put(1, 0); // Using 0 as a sentinel for no parent         Queue<Integer> queue = new LinkedList<>();        queue.add(1);        int level = 0;        boolean found = false;         while (!queue.isEmpty() && !found) {            int levelSize = queue.size();            level++;            int start = 1 << level;            int end = (1 << (level + 1)) - 1;            List<Integer> children = new ArrayList<>();            if (level % 2 == 1) { // Even row number, R-L labeling                for (int i = end; i >= start; i--) {                    children.add(i);                }            } else { // Odd row number, L-R labeling                for (int i = start; i <= end; i++) {                    children.add(i);                }            }             for (int i = 0; i < levelSize; i++) {                int parent = queue.poll();                int child1 = children.get(2 * i);                int child2 = children.get(2 * i + 1);                parentMap.put(child1, parent);                parentMap.put(child2, parent);                queue.add(child1);                queue.add(child2);                if (child1 == label || child2 == label) {                    found = true;                }            }        }         LinkedList<Integer> path = new LinkedList<>();        int current = label;        while (current != 0) {            path.addFirst(current);            current = parentMap.get(current);        }        return path;    }}

Complexity

Time

O(label) - To find the path for a given `label`, we must construct the tree up to the level of that label. The number of nodes in the tree grows exponentially with the level. The total number of nodes processed is proportional to the value of `label` itself.

Space

O(label) - The space required is dominated by the `parentMap` and the queue used for level-order traversal. In the worst case, we might store information for all nodes up to the level of the target `label`. The number of nodes up to level `d` is `2^(d+1) - 1`. Since `d` is `log2(label)`, the number of nodes is approximately `2*label`.

Trade-offs

Pros

  • Straightforward to implement as it directly follows the problem's description.

  • Easy to reason about and debug.

Cons

  • Highly inefficient for large labels as both time and space complexity are linear with respect to the label value.

  • Can lead to OutOfMemoryError for labels close to the constraint 10^6.

Solutions

class Solution {public  List<Integer> pathInZigZagTree(int label) {    int x = 1, i = 1;    while ((x << 1) <= label) {      x <<= 1;      ++i;    }    List<Integer> ans = new ArrayList<>();    for (; i > 0; --i) {      ans.add(label);      label = ((1 << (i - 1)) + (1 << i) - 1 - label) >> 1;    }    Collections.reverse(ans);    return ans;  }}

Video walkthrough

Newsletter

One sharp idea, every week

System design and interview prep — short enough to finish.

No spam. Unsubscribe anytime.

Practice

Same difficulty — related problems to reinforce the pattern.