Skip to content

Commit bdb2bfe

Browse files
my solution to 2096
1 parent 0756a00 commit bdb2bfe

File tree

1 file changed

+70
-2
lines changed

1 file changed

+70
-2
lines changed

problems/2096/jeremymanning.md

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,79 @@
11
# [Problem 2096: Step-By-Step Directions From a Binary Tree Node to Another](https://leetcode.com/problems/step-by-step-directions-from-a-binary-tree-node-to-another/description/?envType=daily-question)
22

33
## Initial thoughts (stream-of-consciousness)
4+
- I think we can do something like this:
5+
- Do a breadth-first search to find the start and end nodes
6+
- As we traverse the tree, we'll copy it-- but instead of using the `TreeNode` class, we'll define a new class that lets us store both the parent of each node (like [yesterday's daily problem](https://leetcode.com/problems/create-binary-tree-from-descriptions/description/?envType=daily-question)) *and* whether the current node is the left or right child of its parent.
7+
- Once we find both the start and end nodes, we can stop this process (but in the worst case, we'll need to traverse the full tree, which will take $O(n)$ time, where $n$ is the number of nodes-- i.e., we need to visit each node up to one time)
8+
- Next we need to find a common ancester of both nodes:
9+
- Lets start from the starting node. Just keep following parents until we get to the root. Store the value of (parent) node that we pass and also its depth (e.g., number of levels up from the starting node).
10+
- Next let's start from the ending node. Again, keep following parents until we get to the root (in the worst case). But this time, things work a little differently:
11+
- each time we visit a node, check if it's on the path from the starting node back to the root. once we hit a common ancestor we can stop looking.
12+
- we'll need to construct the path from the common ancestor down to the end node in reverse order (we could just prepend instructions as we go). if the current node is the right child of its parent, we prepend an "R" to the directions, and so on, until we get to the common ancestor.
13+
- Finally, prepend $n$ `U`s to the directions, where $n$ is the number of "levels up" from the starting node to the common ancestor.
14+
- Now we can just return the directions.
415

516
## Refining the problem, round 2 thoughts
17+
- We will need to account for the possibility that the start and end nodes are the same, and/or that one (or both) is the root of the tree (in which case "0" `'U'` instructions are needed)
18+
- Ok, let's go with this...
619

720
## Attempted solution(s)
821
```python
9-
class Solution: # paste your code here!
10-
...
22+
class TreeNodeWithParentAndDirection(TreeNode):
23+
def __init__(self, val=0, left=None, right=None, parent=None, dir=None):
24+
self.val = val
25+
self.left = left
26+
self.right = right
27+
self.parent = parent
28+
self.dir = dir
29+
30+
class Solution:
31+
def getDirections(self, root: Optional[TreeNode], startValue: int, destValue: int) -> str:
32+
start_node = None
33+
dest_node = None
34+
35+
# breadth-first search
36+
queue = [TreeNodeWithParentAndDirection(val=root.val, left=root.left, right=root.right)]
37+
while (len(queue) > 0) and (start_node is None or dest_node is None):
38+
next_node = queue.pop(0)
39+
if next_node.val == startValue:
40+
start_node = next_node
41+
42+
if next_node.val == destValue:
43+
end_node = next_node
44+
45+
#enqueue the left and right children
46+
if next_node.left is not None:
47+
queue.append(TreeNodeWithParentAndDirection(val=next_node.left.val, left=next_node.left.left, right=next_node.left.right, parent=next_node, dir='L'))
48+
if next_node.right is not None:
49+
queue.append(TreeNodeWithParentAndDirection(val=next_node.right.val, left=next_node.right.left, right=next_node.right.right, parent=next_node, dir='R'))
50+
51+
# find a path from the start_node back to the root (store in a hash table-- i think this will be better than a list)
52+
start_to_root = {}
53+
node = start_node
54+
count = 0
55+
while node.parent is not None:
56+
start_to_root[node.val] = count
57+
node = node.parent
58+
count += 1
59+
60+
start_to_root[node.val] = count
61+
62+
# now find a path from end_node back to the closest node in start_to_root
63+
node = end_node
64+
directions = ''
65+
while node.val not in start_to_root:
66+
directions = node.dir + directions
67+
node = node.parent
68+
69+
return 'U' * start_to_root[node.val] + directions
1170
```
71+
- Given test cases pass
72+
- Let's make up another (larger) tree:
73+
- `root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]`, `startValue = 3`, `destValue = 6`: pass
74+
- `root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]`, `startValue = 21`, `destValue = 14`: pass
75+
- Ok, I think we've got this; submitting...
76+
77+
![Screenshot 2024-07-15 at 11 59 00 PM](https://github.com/user-attachments/assets/1f860f6c-8f50-4a55-a1ba-f9e588db4d25)
78+
79+
- Solved! I'm a little surprised that (a) the runtime is worse than most other solutions-- I'm pretty sure this solution is $O(n)$, and also that (b) the memory use is *better* than most solutions (since we end up potentially copying the entire tree). But (like with yesterday's puzzle) maybe there's an overhead to creating new instances of a class?

0 commit comments

Comments
 (0)