Binary Tree Paths
All root-to-leaf paths in a binary tree — DFS with path-building.
3 min read
backtracking tree dfs
Problem#
Return all root-to-leaf paths as strings ("1->2->3" format).
Examples#
[1,2,3,null,5]→["1->2->5","1->3"]
Constraints#
1 <= n <= 100.
Approach#
DFS carrying the current path. At a leaf, emit. The path can be passed by reference with push/pop, or by value (creating a new string each recursion).
Solution#
class Solution {public: vector<string> binaryTreePaths(TreeNode* root) { vector<string> out; function<void(TreeNode*, string)> dfs = [&](TreeNode* n, string path) { if (!n) return; if (!path.empty()) path += "->"; path += to_string(n->val); if (!n->left && !n->right) { out.push_back(path); return; } dfs(n->left, path); dfs(n->right, path); }; dfs(root, ""); return out; }};func binaryTreePaths(root *TreeNode) []string { out := []string{} var dfs func(n *TreeNode, path string) dfs = func(n *TreeNode, path string) { if n == nil { return } if path != "" { path += "->" } path += strconv.Itoa(n.Val) if n.Left == nil && n.Right == nil { out = append(out, path) return } dfs(n.Left, path) dfs(n.Right, path) } dfs(root, "") return out}from typing import List, Optional
class Solution: def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]: out: List[str] = []
def dfs(n: Optional[TreeNode], path: str) -> None: if not n: return if path: path += "->" path += str(n.val) if not n.left and not n.right: out.append(path) return dfs(n.left, path) dfs(n.right, path)
dfs(root, "") return outvar binaryTreePaths = function(root) { const out = []; const dfs = (n, path) => { if (!n) return; if (path !== "") path += "->"; path += String(n.val); if (!n.left && !n.right) { out.push(path); return; } dfs(n.left, path); dfs(n.right, path); }; dfs(root, ""); return out;};class Solution { private List<String> out;
private void dfs(TreeNode n, String path) { if (n == null) return; if (!path.isEmpty()) path += "->"; path += n.val; if (n.left == null && n.right == null) { out.add(path); return; } dfs(n.left, path); dfs(n.right, path); }
public List<String> binaryTreePaths(TreeNode root) { out = new ArrayList<>(); dfs(root, ""); return out; }}function binaryTreePaths(root: TreeNode | null): string[] { const out: string[] = []; const dfs = (n: TreeNode | null, path: string): void => { if (!n) return; if (path !== "") path += "->"; path += String(n.val); if (!n.left && !n.right) { out.push(path); return; } dfs(n.left, path); dfs(n.right, path); }; dfs(root, ""); return out;}Editorial#
Passing path by value avoids the explicit pop on backtrack — each recursive call gets its own copy. For larger paths, pass by reference and manage push/pop manually.
Complexity#
- Time: O(n).
- Space: O(n).
Concept revision#
Related#