Katkat

Maintain an Out-of-Order Step Progress Tree

Ai2Software Engineer · Technical Screen

Last updated

This problem tests tree reconstruction from a stream of out-of-order parent-child updates, requiring efficient hierarchical state management and cycle detection.

Problem Statement

You are building a real-time progress tracker for complex workflows. You receive a stream of StepUpdate events, where each event updates the state of a step or defines its parent relationship. Steps can be created in any order, and parent references may refer to steps that have not been seen yet.

An event is defined as: {step_id: str, status: str, parent_id: str | None, timestamp: int}.

Requirements

  1. Hierarchy: If a step is assigned a parent_id, it becomes a child of that step. If a step is reassigned to a new parent, it moves in the tree.
  2. Out-of-order: Updates for a child might arrive before the parent exists, or later updates might arrive with older timestamps. Always honor the state of the update with the highest timestamp for a specific step_id.
  3. Cycle Prevention: If a parent_id assignment creates a cycle, ignore that specific assignment (keep the previous valid parent) and log an error.
  4. Snapshot: Implement a method get_tree() that returns a nested dictionary representation of the current valid forest, where keys are step_id and values are their respective children.

Function Signature

class ProgressTracker:
    def update(self, event: dict) -> None:
        # Update internal state with new event

    def get_tree(self) -> dict:
        # Returns the current forest representation

Constraints

  • step_id is unique.
  • A step can have at most one parent.
  • Updates are processed sequentially by the system, but the timestamp field determines which update is considered the "latest" state for a given step_id.
  • You must handle orphan nodes (steps whose parent does not exist yet) by keeping them in the forest until their parent arrives.

Example

# Update 1: {step_id: "B", parent_id: "A", timestamp: 10}
# Update 2: {step_id: "A", parent_id: None, timestamp: 5}
# Result of get_tree(): {"A": ["B"]}

Edge Cases

  • A step claiming to be its own parent (direct cycle).
  • A parent ID that never appears in the event stream.

Hints

Hint 1

Maintain a map of `step_id` to its current `latest_event` to ensure only the most recent data is used.

Hint 2

Use an adjacency list to track children and a separate map to track the current parent for quick lookup and cycle validation.

Hint 3

When processing a new `parent_id` for a node, perform a search from the target parent upwards to the root to ensure the node is not already an ancestor of the target parent.

Follow-up questions

  • How would you optimize the `get_tree()` call if the tree structure is massive but only a small branch changes?
  • How would you ensure thread safety if multiple threads are calling `update()` concurrently?
  • What changes would be required if we needed to support multiple parents per step (a Directed Acyclic Graph instead of a tree)?