Forgetting null/base-case handling
Wrong move: Recursive traversal assumes children always exist.
Usually fails on: Leaf nodes throw errors or create wrong depth/path values.
Fix: Handle null/base cases before recursive transitions.
Move from brute-force thinking to an efficient approach using tree strategy.
You are given the root of a binary tree with n nodes. Each node is uniquely assigned a value from 1 to n. You are also given an integer startValue representing the value of the start node s, and a different integer destValue representing the value of the destination node t.
Find the shortest path starting from node s and ending at node t. Generate step-by-step directions of such path as a string consisting of only the uppercase letters 'L', 'R', and 'U'. Each letter indicates a specific direction:
'L' means to go from a node to its left child node.'R' means to go from a node to its right child node.'U' means to go from a node to its parent node.Return the step-by-step directions of the shortest path from node s to node t.
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:
n.2 <= n <= 1051 <= Node.val <= n1 <= startValue, destValue <= nstartValue != destValueProblem summary: You are given the root of a binary tree with n nodes. Each node is uniquely assigned a value from 1 to n. You are also given an integer startValue representing the value of the start node s, and a different integer destValue representing the value of the destination node t. Find the shortest path starting from node s and ending at node t. Generate step-by-step directions of such path as a string consisting of only the uppercase letters 'L', 'R', and 'U'. Each letter indicates a specific direction: 'L' means to go from a node to its left child node. 'R' means to go from a node to its right child node. 'U' means to go from a node to its parent node. Return the step-by-step directions of the shortest path from node s to node t.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Tree
[5,1,2,3,null,6,4] 3 6
[2,1] 2 1
path-sum-ii)lowest-common-ancestor-of-a-binary-tree)binary-tree-paths)find-distance-in-a-binary-tree)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #2096: Step-By-Step Directions From a Binary Tree Node to Another
class Solution {
public String getDirections(TreeNode root, int startValue, int destValue) {
TreeNode node = lca(root, startValue, destValue);
StringBuilder pathToStart = new StringBuilder();
StringBuilder pathToDest = new StringBuilder();
dfs(node, startValue, pathToStart);
dfs(node, destValue, pathToDest);
return "U".repeat(pathToStart.length()) + pathToDest.toString();
}
private TreeNode lca(TreeNode node, int p, int q) {
if (node == null || node.val == p || node.val == q) {
return node;
}
TreeNode left = lca(node.left, p, q);
TreeNode right = lca(node.right, p, q);
if (left != null && right != null) {
return node;
}
return left != null ? left : right;
}
private boolean dfs(TreeNode node, int x, StringBuilder path) {
if (node == null) {
return false;
}
if (node.val == x) {
return true;
}
path.append('L');
if (dfs(node.left, x, path)) {
return true;
}
path.setCharAt(path.length() - 1, 'R');
if (dfs(node.right, x, path)) {
return true;
}
path.deleteCharAt(path.length() - 1);
return false;
}
}
// Accepted solution for LeetCode #2096: Step-By-Step Directions From a Binary Tree Node to Another
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func getDirections(root *TreeNode, startValue int, destValue int) string {
var lca func(node *TreeNode, p, q int) *TreeNode
lca = func(node *TreeNode, p, q int) *TreeNode {
if node == nil || node.Val == p || node.Val == q {
return node
}
left := lca(node.Left, p, q)
right := lca(node.Right, p, q)
if left != nil && right != nil {
return node
}
if left != nil {
return left
}
return right
}
var dfs func(node *TreeNode, x int, path *[]byte) bool
dfs = func(node *TreeNode, x int, path *[]byte) bool {
if node == nil {
return false
}
if node.Val == x {
return true
}
*path = append(*path, 'L')
if dfs(node.Left, x, path) {
return true
}
(*path)[len(*path)-1] = 'R'
if dfs(node.Right, x, path) {
return true
}
*path = (*path)[:len(*path)-1]
return false
}
node := lca(root, startValue, destValue)
pathToStart := []byte{}
pathToDest := []byte{}
dfs(node, startValue, &pathToStart)
dfs(node, destValue, &pathToDest)
return string(bytes.Repeat([]byte{'U'}, len(pathToStart))) + string(pathToDest)
}
# Accepted solution for LeetCode #2096: Step-By-Step Directions From a Binary Tree Node to Another
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def getDirections(
self, root: Optional[TreeNode], startValue: int, destValue: int
) -> str:
def lca(node: Optional[TreeNode], p: int, q: int):
if node is None or node.val in (p, q):
return node
left = lca(node.left, p, q)
right = lca(node.right, p, q)
if left and right:
return node
return left or right
def dfs(node: Optional[TreeNode], x: int, path: List[str]):
if node is None:
return False
if node.val == x:
return True
path.append("L")
if dfs(node.left, x, path):
return True
path[-1] = "R"
if dfs(node.right, x, path):
return True
path.pop()
return False
node = lca(root, startValue, destValue)
path_to_start = []
path_to_dest = []
dfs(node, startValue, path_to_start)
dfs(node, destValue, path_to_dest)
return "U" * len(path_to_start) + "".join(path_to_dest)
// Accepted solution for LeetCode #2096: Step-By-Step Directions From a Binary Tree Node to Another
/**
* [2096] Step-By-Step Directions From a Binary Tree Node to Another
*
* You are given the root of a binary tree with n nodes. Each node is uniquely assigned a value from 1 to n. You are also given an integer startValue representing the value of the start node s, and a different integer destValue representing the value of the destination node t.
* Find the shortest path starting from node s and ending at node t. Generate step-by-step directions of such path as a string consisting of only the uppercase letters 'L', 'R', and 'U'. Each letter indicates a specific direction:
*
* 'L' means to go from a node to its left child node.
* 'R' means to go from a node to its right child node.
* 'U' means to go from a node to its parent node.
*
* Return the step-by-step directions of the shortest path from node s to node t.
*
* Example 1:
* <img alt="" src="https://assets.leetcode.com/uploads/2021/11/15/eg1.png" style="width: 214px; height: 163px;" />
* 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:
* <img alt="" src="https://assets.leetcode.com/uploads/2021/11/15/eg2.png" style="width: 74px; height: 102px;" />
* 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 <= 10^5
* 1 <= Node.val <= n
* All the values in the tree are unique.
* 1 <= startValue, destValue <= n
* startValue != destValue
*
*/
pub struct Solution {}
use crate::util::tree::{TreeNode, to_tree};
// problem: https://leetcode.com/problems/step-by-step-directions-from-a-binary-tree-node-to-another/
// discuss: https://leetcode.com/problems/step-by-step-directions-from-a-binary-tree-node-to-another/discuss/?currentPage=1&orderBy=most_votes&query=
// submission codes start here
// Definition for a binary tree node.
// #[derive(Debug, PartialEq, Eq)]
// pub struct TreeNode {
// pub val: i32,
// pub left: Option<Rc<RefCell<TreeNode>>>,
// pub right: Option<Rc<RefCell<TreeNode>>>,
// }
//
// impl TreeNode {
// #[inline]
// pub fn new(val: i32) -> Self {
// TreeNode {
// val,
// left: None,
// right: None
// }
// }
// }
use std::cell::RefCell;
use std::rc::Rc;
impl Solution {
pub fn get_directions(
root: Option<Rc<RefCell<TreeNode>>>,
start_value: i32,
dest_value: i32,
) -> String {
String::new()
}
}
// submission codes end
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore]
fn test_2096_example_1() {
let root = tree![5, 1, 2, 3, null, 6, 4];
let start_value = 3;
let dest_value = 6;
let result = "UURL".to_string();
assert_eq!(
Solution::get_directions(root, start_value, dest_value),
result
);
}
#[test]
#[ignore]
fn test_2096_example_2() {
let root = tree![2, 1];
let start_value = 2;
let dest_value = 1;
let result = "L".to_string();
assert_eq!(
Solution::get_directions(root, start_value, dest_value),
result
);
}
}
// Accepted solution for LeetCode #2096: Step-By-Step Directions From a Binary Tree Node to Another
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
function getDirections(root: TreeNode | null, startValue: number, destValue: number): string {
const lca = (node: TreeNode | null, p: number, q: number): TreeNode | null => {
if (node === null || [p, q].includes(node.val)) {
return node;
}
const left = lca(node.left, p, q);
const right = lca(node.right, p, q);
return left && right ? node : (left ?? right);
};
const dfs = (node: TreeNode | null, x: number, path: string[]): boolean => {
if (node === null) {
return false;
}
if (node.val === x) {
return true;
}
path.push('L');
if (dfs(node.left, x, path)) {
return true;
}
path[path.length - 1] = 'R';
if (dfs(node.right, x, path)) {
return true;
}
path.pop();
return false;
};
const node = lca(root, startValue, destValue);
const pathToStart: string[] = [];
const pathToDest: string[] = [];
dfs(node, startValue, pathToStart);
dfs(node, destValue, pathToDest);
return 'U'.repeat(pathToStart.length) + pathToDest.join('');
}
Use this to step through a reusable interview workflow for this problem.
BFS with a queue visits every node exactly once — O(n) time. The queue may hold an entire level of the tree, which for a complete binary tree is up to n/2 nodes = O(n) space. This is optimal in time but costly in space for wide trees.
Every node is visited exactly once, giving O(n) time. Space depends on tree shape: O(h) for recursive DFS (stack depth = height h), or O(w) for BFS (queue width = widest level). For balanced trees h = log n; for skewed trees h = n.
Review these before coding to avoid predictable interview regressions.
Wrong move: Recursive traversal assumes children always exist.
Usually fails on: Leaf nodes throw errors or create wrong depth/path values.
Fix: Handle null/base cases before recursive transitions.