LeetCode #257 Medium

Binary Tree Paths

Return every root-to-leaf path as strings like "1->2->5".

treedfsbacktracking
Open on LeetCode ↗
02

Intuition

DFS carrying the path so far. At a leaf, freeze the path into a string. Passing the accumulated string down each call (or a list with push/pop backtracking) enumerates each path exactly once.

How to spot this pattern

Root-to-leaf enumeration. Because strings are immutable in Python, passing prefix + "->" gives you backtracking for free — each branch receives its own copy and there is nothing to undo. That's worth recognising: when the accumulator is immutable, the explicit pop of a normal backtracking loop disappears.

03

Approach

1

Carry the prefix

dfs(node, prefix): extend prefix with node.val; at a leaf, emit.

2

Leaf is the emit point

Only nodes with no children terminate a path — internal nodes just pass through.

3

Strings vs backtracking

String concatenation copies per call (fine for output-sized work); a shared list with pop() after recursion avoids copies.

04

Solution & live demo

1class Solution:
2 def binaryTreePaths(self, root):
3 res = []
4 def dfs(node, prefix):
5 if not node: return
6 prefix += str(node.val)
7 if not node.left and not node.right:
8 res.append(prefix); return
9 dfs(node.left, prefix + "->")
10 dfs(node.right, prefix + "->")
11 dfs(root, "")
12 return res
05

Common pitfalls

Building the path in a shared list without undoing

✗ Wrong
path.append(str(node.val))
dfs(node.left)
dfs(node.right)
✓ Right
dfs(node.left, prefix + "->")
dfs(node.right, prefix + "->")

A shared list carries nodes from abandoned branches into unrelated paths unless every append is matched by a pop. Passing an immutable string sidesteps the whole class of bug — each call frame owns its prefix.

Appending the arrow after the leaf

✗ Wrong
prefix += str(node.val) + "->"
if not node.left and not node.right:
    res.append(prefix)
✓ Right
prefix += str(node.val)
if not node.left and not node.right:
    res.append(prefix); return
dfs(node.left, prefix + "->")

Every recorded path would end with a trailing "->". The separator belongs between values, so it's added when descending to a child — not after writing a node.

Recording at every node instead of at leaves

✗ Wrong
prefix += str(node.val)
res.append(prefix)
✓ Right
if not node.left and not node.right:
    res.append(prefix); return

The problem asks for root-to-leaf paths, so partial paths ending at internal nodes don't count. Recording everywhere returns every prefix of every path.

06

Edge cases

Single node

Root is a leaf → ["root"].

Node with one child

Not a leaf — path continues through the existing child only.

07

Complexity

Time
O(n·h)
Space
O(h)
Each path string costs its length.