2096. Step-By-Step Directions From a Binary Tree Node to Another
Problem
Intuition
This is a very nice problem as one require multiple concepts. Like
Lowest Common Ancestor
DFS : Tree Traversal and Backtracking (to get Path b/w nodes)
First let us make couple of observations and note it down :
How to get shortest distance/path b/w two node in Binary Tree?
We need to find closest point(from nodes), where path from root to nodes intersect
This is nothing but the LCA(Lowest Common Ancestor) of two nodes.
Now, if we move from start node to LCA node, which direction we will follow :
U(up)
,L(left)
orR(right)
?Here we can surely say since LCA always lie above the node, so we will move in upward direction.
And there can also be a case when start node itself is LCA, so no need to worry in this case.
Now coming back to our problem, we need to get shortest path from
start -> destination
, orin other words path from
"start -> LCA" + "LCA -> destination" -> But we cannot find "start -> LCA" directly. i.e from bottom to up -> What we can do instead is first find top to down path i.e "LCA -> start" , using normal tree traversal algo -> and then convert it such that we move in upward direction. (Observation: 2)
Thus, putting all these together. Lets write a step-by-step algorithm to get directions :
First of all find the LCA node of
start
anddestination
.Then we need to get path from
lca_to_start
and fromlca_to_destination
.To get path from
root -> node
as a string, we will usetraverse()
function.In this we simply do a simple DFS ( kind of pre-order traversal)
First explore left path, if node is found. Then return true.
Otherwise backtrack and explore right path.
Now that we have both paths, we will convert all chars in
lca_to_start
toU
, since we will move upward.At last, concatenate both strings and return combined path.
Lets see this through an example :
Given : start = 3, destination = 6 5 ---> LCA(3,6) / \ 1 2 / / \ start <--- '3' '6' 4 ^ | destination -> So here node '5' is LCA of start(3) and dest(3) -> path from lca_to_start = '5 -> 1 -> 3' = "L L" -> path from lca_to_dest = '5 -> 2 -> 6' = "R L" Also, since we know that shortest path from # start-> destination = 'start -> LCA' + 'LCA -> destination' So, we need to convert (reverse) `lca_to_start` = "U U" , as we move in upward direction Therefore, final path = "U U" + "R L" = "U U R L"
Time Complexity
N -> Total Number of node
O(N) -> LCA
O(N) -> Traverse from LCA to start
O(N) -> Traverse from LCA to dest
Space Complexity
N -> Total Number of node
O(h) -> LCA stack space
O(N) -> In worst case, there is a skewed tree and the path from left most node to right most // node is O(N)
Solution
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public String getDirections(TreeNode root, int startValue, int destValue) {
// Time Complexity:
// O(N) -> LCA, O(N) -> Traverse from LCA to start, O(N) -> Traverse from LCA to des
// O(N) + O(N) + O(N) = 3 O(N) = O(N)
// Space Complexity:
// O(h) -> LCA stack space
// O(N) -> In worst case, there is a skewed tree and the path from left most node to right most
// node is O(N)
// O(h) + O(N) = O(N)
TreeNode lca = lcaHelper(root, startValue, destValue);
List<String> start = new ArrayList<>();
List<String> end = new ArrayList<>();
String s = "";
traverse(lca, start, startValue);
traverse(lca, end, destValue);
// Whatever the path is from LCA to startval, we explored it downwards using "L" and "R"
// But if we have to chart a path from start to LCA, then we only need to move "UP"
// So we replace the path with equivalent number of steps to UP
for(String dir: start) s += "U";
// Append the directions from LCA to destination value
for(String dir: end) s += dir;
return s;
}
private boolean traverse(TreeNode node, List<String> steps, int destVal) {
// If node is null, nothing more to traverse, we did not reach the destination
// So return false
if(node == null) return false;
// If we reach a node where its value is equal to destination value,
// then return true.
// The constraint of the question is all the nodes have unique values
if(node.val == destVal) return true;
// We perform traversal by checking out Left and Right
steps.add("L"); // Add "L" to the step for exploring Left side
// If we found the destination value by exploring left subtree, return true
if(traverse(node.left, steps, destVal)) return true;
// Once the left subtree is completely explored, then we return to the node
// So we remove the last "L" step
steps.remove(steps.size() - 1);
steps.add("R"); // Add "R" to the step for exploring Right side
// If we found the destination value by exploring right subtree, return true
if(traverse(node.right, steps, destVal)) return true;
// Once the right subtree is completely explored, then we return to the node
// So we remove the last "R" step
steps.remove(steps.size() - 1);
return false;
}
private TreeNode lcaHelper(TreeNode root, int start, int dest) {
// Standard LCA algorithm
if(root == null) return null;
if(root.val == start || root.val == dest) return root;
TreeNode left = lcaHelper(root.left, start, dest);
TreeNode right = lcaHelper(root.right, start, dest);
if(left == null) return right;
else if(right == null) return left;
else return root;
}
}
Last updated
Was this helpful?