Step-By-Step Directions From a Binary Tree Node to Another

Updated on 14 July, 2025
Step-By-Step Directions From a Binary Tree Node to Another header image

Problem Statement

In this problem, we are provided with a binary tree where each node has a unique integer value between 1 and n, and n is the total number of nodes in the tree. Additionally, we are given two integers, startValue and destValue, which denote the values of the start and destination nodes respectively in the binary tree. The goal is to determine the shortest path from the node with value startValue to the node with value destValue and express this path using a sequence of directional steps encoded as characters:

  • 'L' indicates moving to the left child of the current node.
  • 'R' indicates moving to the right child of the current node.
  • 'U' indicates moving to the parent of the current node.

The task is to return the sequence of moves as a string that will take us from the start node to the destination node using the shortest path possible.

Examples

Example 1

Input:

root = [5,1,2,3,null,6,4], startValue = 3, destValue = 6

Output:

"UURL"

Explanation:

The shortest path is: 3 → 1 → 5 → 2 → 6.

Example 2

Input:

root = [2,1], startValue = 2, destValue = 1

Output:

"L"

Explanation:

The shortest path is: 2 → 1.

Constraints

  • The number of nodes in the tree is n.
  • 2 <= n <= 105
  • 1 <= Node.val <= n
  • All the values in the tree are unique.
  • 1 <= startValue, destValue <= n
  • startValue != destValue

Approach and Intuition

Step-by-Step Explanation

  1. Mapping Nodes and Building Path to Root:
    To solve the problem, an effective approach is to create a map that links every node in the tree to its parent. This will help in tracing the path back up the tree from any given node to the root.

  2. Find Paths from Start and Destination to Root:

    • Start from the startValue node and follow the parent links up to the root, recording each move as 'U', to formulate the path.
    • Similarly, trace the path from the destValue node up to the root.
  3. Calculate Intersection Point:

    • Since we have two paths that move up to the root, these paths will share a common segment from some point up to the root, which we can find by comparing the two paths from the root downwards.
    • The first common node encountered is the branching point from where we can trace paths down to both the start and destination nodes.
  4. Build the Final Path Sequence:

    • Traverse backwards from the start node to this common ancestor, using the 'U' steps.
    • From the common ancestor, move towards the destination node. This will require translating the path down from the ancestor to the destination node into 'L' and 'R' steps, depending on whether the movement is to the left or right child.
  5. Combining the Paths:

    • Combine the upward moves from the start node to the common ancestor and the downward moves from the ancestor to the destination node to form the complete path.

Example Walkthrough

In the provided example where the binary tree is [5,1,2,3,null,6,4] with startValue = 3 and destValue = 6, the path calculation would proceed as outlined:

  • Trace from 3 to its root: 3 (Parent 1) → 1 (Parent 5) → 5.
  • Trace from 6 to its root: 6 (Parent 2) → 2 (Parent 5) → 5.
  • The common ancestor is node 5.
  • We find path 3 to 5 as 'UU' and 5 to 6 as 'RL'. Combining these, we get 'UURL', which directs us from node 3 to 6 via node 5.

This approach ensures we efficiently find the shortest path utilizing properties of the tree structure and parent-child relationships.

Solutions

  • C++
cpp
class Solution {
public:
    string computePath(TreeNode* root, int start, int destination) {
        string pathFromRootToStart, pathFromRootToDestination;
    
        // Locate unique paths from root to given nodes
        locatePath(root, start, pathFromRootToStart);
        locatePath(root, destination, pathFromRootToDestination);
    
        string route;
        int sharedPathLength = 0;
    
        // Determine shared path segment length
        while (sharedPathLength < pathFromRootToStart.length() &&
               sharedPathLength < pathFromRootToDestination.length() &&
               pathFromRootToStart[sharedPathLength] == pathFromRootToDestination[sharedPathLength]) {
            sharedPathLength++;
        }
    
        // Trace steps back to the common ancestor
        for (int i = 0; i < pathFromRootToStart.length() - sharedPathLength; i++) {
            route += "U";
        }
    
        // Append path from common ancestor to the destination
        for (int i = sharedPathLength; i < pathFromRootToDestination.length(); i++) {
            route += pathFromRootToDestination[i];
        }
    
        return route;
    }
    
private:
    bool locatePath(TreeNode* node, int value, string& path) {
        if (!node) return false;
    
        if (node->val == value) return true;
    
        // Navigate left
        path += "L";
        if (locatePath(node->left, value, path)) return true;
        path.pop_back();  // Undo last step if incorrect
    
        // Navigate right
        path += "R";
        if (locatePath(node->right, value, path)) return true;
        path.pop_back();  // Undo last step if incorrect
    
        return false;
    }
};

The provided C++ code defines a solution for finding a path between two nodes in a binary tree. This is summarized step-by-step below:

  1. The main function computePath takes three parameters: the root of the tree, and two integers representing the start and destination nodes.
  2. Two strings, pathFromRootToStart and pathFromRootToDestination, store the paths from the root to the start and destination nodes respectively.
  3. The function locatePath is called twice, once for each target node (start and destination), to fill the respective path strings.
  4. A variable route is initialized to record the direct path from the start node to the destination node after accounting for any common path segments.
  5. The initial shared path length is calculated by comparing characters in both path strings until a mismatch occurs.
  6. The segment of the path from the start node up to this divergence point is converted to "U" (up) directions to move up the tree to a common ancestor.
  7. Any remaining segments in the pathFromRootToDestination string from the divergence point to the end are appended directly to route.
  8. Finally, the function returns the computed route string.
  • The helper function locatePath recursively attempts to find a node in the tree.
    • If the node is found, it returns true.
    • It tries to move left, adding an "L" to the path, and returns true if successful; otherwise, it removes the "L" and tries the right with similar logic.
    • If the path exploration is unsuccessful, it backtracks by removing the last step and continues searching.

The completion of this function results in a string detailing the simplest path instructions between two nodes in the tree from the perspective of the start node moving towards the destination node. These instructions are straightforward, using "L" for left, "R" for right, and "U" for up.

  • Java
java
class Solution {
    
    public String routeFromRoot(TreeNode rootNode, int startVal, int endVal) {
        StringBuilder startRoute = new StringBuilder();
        StringBuilder endRoute = new StringBuilder();
    
        // Finding paths from root to the start and end values
        tracePath(rootNode, startVal, startRoute);
        tracePath(rootNode, endVal, endRoute);
    
        StringBuilder finalRoute = new StringBuilder();
        int sharedLength = 0;
    
        // Determine the shared route length
        while (
            sharedLength < startRoute.length() &&
            sharedLength < endRoute.length() &&
            startRoute.charAt(sharedLength) ==
            endRoute.charAt(sharedLength)
        ) {
            sharedLength++;
        }
    
        // Append "U" for each step to go up from the start to their common ancestor
        for (int i = 0; i < startRoute.length() - sharedLength; i++) {
            finalRoute.append("U");
        }
    
        // Append the path from the common ancestor to the destination
        for (int i = sharedLength; i < endRoute.length(); i++) {
            finalRoute.append(endRoute.charAt(i));
        }
    
        return finalRoute.toString();
    }
    
    private boolean tracePath(TreeNode currentNode, int seekValue, StringBuilder pathway) {
        if (currentNode == null) {
            return false;
        }
    
        if (currentNode.val == seekValue) {
            return true;
        }
    
        // Explore left subtree
        pathway.append("L");
        if (tracePath(currentNode.left, seekValue, pathway)) {
            return true;
        }
        pathway.deleteCharAt(pathway.length() - 1); // Undo the last path append
    
        // Explore right subtree
        pathway.append("R");
        if (tracePath(currentNode.right, seekValue, pathway)) {
            return true;
        }
        pathway.deleteCharAt(pathway.length() - 1); // Undo the last path append
    
        return false;
    }
}

In this solution, you find step-by-step directions from one node to another in a binary tree by representing these paths as strings of directions. Employ a Java class where you build and manipulate these directional strings based on tree traversal.

Start by defining a private helper function, tracePath, which recursively finds and records the path to a specific node value as a sequence of 'L' (left) and 'R' (right) steps. The function returns true as soon as it locates the desired node and includes backtracking to ensure only the correct path is stored.

In the main function, routeFromRoot, invoke tracePath twice to determine paths from the root to the starting node (startVal) and the ending node (endVal), respectively. Each path is stored in separate StringBuilder instances.

Calculate the length of the shared path portion by comparing characters at the same positions in both startRoute and endRoute until a mismatch is found, indicating how far up the starting node you need to travel to reach the lowest common ancestor.

Construct the final route by:

  1. Adding "U" to ascend from the starting node up to the common ancestor for each unmatched character in the startRoute.
  2. Appending the remainder of endRoute from the shared path point onward, which leads down to the destination node.

Return the constructed path as a string. This approach ensures only pertinent directions are incorporated, optimizing the traversal and string manipulation processes within a binary tree structure.

  • Python
python
class Solution:
    def getRoutes(
        self, root: TreeNode, startVal: int, destVal: int
    ) -> str:
        start_route = []
        dest_route = []
    
        self._search_path(root, startVal, start_route)
        self._search_path(root, destVal, dest_route)
    
        route = []
        shared_length = 0
    
        while (
            shared_length < len(start_route)
            and shared_length < len(dest_route)
            and start_route[shared_length] == dest_route[shared_length]
        ):
            shared_length += 1
    
        route.extend("U" * (len(start_route) - shared_length))
        route.extend(dest_route[shared_length:])
    
        return "".join(route)
    
    def _search_path(self, node: TreeNode, value: int, route: List[str]) -> bool:
        if node is None:
            return False
    
        if node.val == value:
            return True
    
        route.append("L")
        if self._search_path(node.left, value, route):
            return True
        route.pop()
    
        route.append("R")
        if self._search_path(node.right, value, route):
            return True
        route.pop()
    
        return False

The provided solution in Python details a method for retrieving step-by-step directions from one node to another in a binary tree. The approach includes two main steps determining the route to each node from the root, then establishing the shortest unique path between them. Here's how it breaks down:

  • The getRoutes function takes a binary tree root and two node values (startVal and destVal) as input and returns a string describing the directions. Directions are denoted by 'L' for left and 'R' for right; 'U' is used to move up towards the root.

  • Two helper lists, start_route and dest_route, hold directions from the root to the start and destination nodes, respectively.

  • The function _search_path aids in constructing the routes. It recursively explores the tree, appending to the route list with 'L' or 'R' based on traversal direction. If the target node value is found, it returns true, ceasing further recursion.

  • Once both routes are established, the code identifies the longest common starting segment of the paths, then calculates the unique path by moving up to the common ancestor and then down to the destination node.

  • Movement up to the common ancestor is represented by appending 'U' to the trek for each step up, after which it directly appends the remainder of the destination's path.

This method effectively uses recursion to navigate the tree and dynamically adjusts the route strategy based on the tree's topology, ensuring an efficient and adaptive pathfinding solution.

Comments

No comments yet.