Coding Interview Patterns Module 11 – Tree patterns: depth-first
Treating a tree as a graph
Add parent links to a binary tree and search it like a graph: find every node at distance k from a target, and how long a message takes to reach all nodes.
What you will learn
- Add parent links to search outwards from any node of a binary tree
- Find all nodes at distance k from a target with a breadth-first search
- Compute how long it takes to spread from one node to the whole tree
- Solve both problems without a parent map in one recursive pass, and say what that costs
- Extend the method to several sources, weighted edges and the farthest pair
Before you start
What you’ll learn · Before you start
What you will learn
- Add parent links to search outwards from any node of a binary tree
- Find all nodes at distance k from a target with a breadth-first search
- Compute how long it takes to spread from one node to the whole tree
- Solve both problems without a parent map in one recursive pass, and say what that costs
- Extend the method to several sources, weighted edges and the farthest pair
Before you start
On this page
To find all nodes at distance k from a target in a binary tree, treat the tree as an undirected graph. One traversal records every node’s parent, so each node has up to three neighbours: its left child, its right child and its parent. A breadth-first search from the target then meets the nodes in order of distance, and the nodes of its k-th level are the answer. A set of nodes already reached keeps the search from walking straight back along a link. The same search also gives the time a spread from one node needs to reach all of them.
Two questions about one building
The rooms of a building form a binary tree: the entrance hall is the root, and every room leads on to at most two rooms, each with a unique number.
- Rooms near an alarm. Given the alarm room and a number k, return every room exactly k corridors from it, in increasing order.
- A message to every room. A message starts in one room and crosses one corridor a minute, in both directions. After how many minutes has every room heard it?
Limits: up to 10⁵ rooms, 0 ≤ k ≤ 10⁵, and a building that may be one long corridor.
The building; the rooms two corridors from the alarm in 16 are dashed
Text description of the diagram
The diagram shows eleven rooms as a binary tree, with the entrance hall, room 20, at the top. The corridors are drawn as plain lines without arrows, because they can be walked in both directions. Each line is marked L for the left room below or R for the right room below.
- Room 20 leads to 11 on the left and 35 on the right.
- Room 11 leads to 4, a dead end, and to 16. Room 16 leads to 13, a dead end, and to 18, which leads to 17.
- Room 35 leads to 28, which leads on to 31 on its right, and to 42, a dead end.
The level-order list of this tree is [20, 11, 35, 4, 16, 28, 42, None, None, 13, 18, None, 31, None, None, None, None, 17].
The alarm is in room 16, drawn with a double border. The rooms exactly two corridors away from it are dashed: 17, two corridors down through 18; 4, up to 11 and down again; and 20, two corridors up. A search that only looks below 16 finds 17 alone.
With the alarm in 16, the rooms 2 corridors away are 4, 17 and 20, only one of them below 16; at k = 3 from 31 they are 20 and 42. A message from 17 needs 7 minutes, the last room being 31; from the entrance it needs 4.
The brute force, and why it fails at this size
Measure each room on its own: search from the entrance for the room and for the alarm, and count the corridors where the two paths part. For the message, walk the whole building once a minute, passing it along every corridor next to a room that has it. Both are correct, but the first costs one O(n) search per room and the second one walk per minute, with n minutes on a long corridor. The run counts 501,500 and 999,000 rooms visited on a corridor of 1,000; at 10⁵ rooms that grows to about 5 × 10⁹ and 10¹⁰.
"""Rooms at distance k, and the time for news to reach every room, by brute force; the rooms visited are counted."""
from collections import deque
class Node:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
def build(values):
"""The tree of a level-order list, where None marks a missing child."""
if not values or values[0] is None:
return None
root = Node(values[0])
waiting = deque([root])
i = 1
while i < len(values):
node = waiting.popleft()
for side in ("left", "right"):
if i < len(values) and values[i] is not None:
child = Node(values[i])
setattr(node, side, child)
waiting.append(child)
i += 1
return root
visits = 0
def path_to(root, room):
"""The rooms from the entrance down to room, found by searching the building from the top."""
global visits
above, stack = {root: None}, [root]
while stack:
node = stack.pop()
visits += 1
if node.val == room:
path = []
while node:
path.append(node.val)
node = above[node]
return path[::-1]
for child in (node.right, node.left):
if child:
above[child] = node
stack.append(child)
return None
def every_room(root):
out, stack = [], [root]
while stack:
node = stack.pop()
out.append(node.val)
stack += [c for c in (node.right, node.left) if c]
return out
def at_distance_brute(root, target, k):
"""For every room, find both paths from the entrance and measure the distance where they part."""
to_target = path_to(root, target)
found = []
for room in every_room(root):
to_room = path_to(root, room)
shared = 0
while shared < min(len(to_room), len(to_target)) and to_room[shared] == to_target[shared]:
shared += 1
if (len(to_room) - shared) + (len(to_target) - shared) == k:
found.append(room)
return sorted(found)
def minutes_brute(root, start):
"""Each minute, walk the whole building once and pass the news across every corridor next to someone who knows."""
global visits
heard, minutes, total = {start}, 0, len(every_room(root))
while len(heard) < total:
newly = set()
stack = [(root, None)]
while stack:
node, above = stack.pop()
visits += 1
near = [c.val for c in (node.left, node.right) if c] + ([above] if above is not None else [])
if node.val not in heard and any(r in heard for r in near):
newly.add(node.val)
stack += [(c, node.val) for c in (node.right, node.left) if c]
heard |= newly
minutes += 1
return minutes
building = build([20, 11, 35, 4, 16, 28, 42, None, None, 13, 18, None, 31, None, None, None, None, 17])
for target, k in [(16, 2), (31, 3), (16, 0)]:
visits = 0
print(f"rooms {k} corridors from {target}: {at_distance_brute(building, target, k)}, {visits} rooms visited")
for start in (17, 20):
visits = 0
print(f"minutes for news from {start} to reach every room: {minutes_brute(building, start)}, {visits} rooms visited")
line = node = Node(0)
for v in range(1, 1000):
node.left = Node(v)
node = node.left
visits = 0
print(f"a line of 1,000, news from the far end: {minutes_brute(line, 999)} minutes, {visits:,} rooms visited")
visits = 0
at_distance_brute(line, 999, 10)
print(f"a line of 1,000, rooms 10 corridors from the far end: {visits:,} rooms visited") Output
rooms 2 corridors from 16: [4, 17, 20], 70 rooms visited rooms 3 corridors from 31: [20, 42], 76 rooms visited rooms 0 corridors from 16: [16], 70 rooms visited minutes for news from 17 to reach every room: 7, 77 rooms visited minutes for news from 20 to reach every room: 4, 44 rooms visited a line of 1,000, news from the far end: 999 minutes, 999,000 rooms visited a line of 1,000, rooms 10 corridors from the far end: 501,500 rooms visited
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 brute_force.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
What to notice
The way between two rooms may climb towards the entrance before it turns down again, and the tree records only the way down.
- A parent map gives every node a link to its parent, after one traversal.
- A breadth-first search over left, right and parent links meets nodes in order of distance.
- A visited set stops the search from walking back along the link it came by.
- The number of levels after the first is how long a spread from that node takes.
- One recursive pass can report each node’s distance to the target instead, with no parent map.
- Several sources at once start the same search with every source on its first level.
Parent links turn the tree into a graph Pro: opens with a Pro, Premium or Ultimate pass
In this part:
- Why the levels are exact distances, with an invariant
- How long until every room hears
- Without a parent map: one recursive pass
- The code in five languages
- Edge cases and common bugs
- Variants: within k, several alarms, lengths, the farthest pair
- Follow-up questions
- Exercise
- Minutes until a message reaches every room
- A practice set with sample answers
- Check yourself
- Interview questions
- Why does a breadth-first search over a binary tree with parent links need a set of visited nodes, when a search from the root down does not?
- Find all nodes at distance k from a target in a binary tree without a parent map and without changing the nodes. What does each recursive call return, how are the answers collected, and how much memory does it use?
- News starts with several employees of an org chart at the same moment and crosses one reporting link a minute. How do you compute when the last person hears it, and how does the cost change as the number of starting points grows from 1 to n?
- A recursive distance-k solution passes small tests but crashes on a chain of 10⁵ nodes in Python and in JavaScript. Explain the failure, and rewrite the approach so that no call stack grows with the height of the tree.
- Key takeaways
8 runnable examples (C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 21 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
References
- NIST Dictionary of Algorithms and Data Structures: breadth-first search (National Institute of Standards and Technology (NIST))
- collections.deque (The Python Standard Library) (Python Software Foundation)
- sys.getrecursionlimit() (The Python Standard Library) (Python Software Foundation)
- Java SE 25 API: HashSet.add (Oracle)
- std::unordered_set::insert (cppreference.com) (cppreference.com)
- The Go Programming Language Specification: index expressions (The Go Authors)
- ECMAScript 2026 Language Specification: Set objects (Ecma International)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress