Trees
Step 11 in the DSA path · 23 concepts · 72 problems
📘 Learn Trees from zero
The Tree DFS pattern walks a binary tree recursively (root → left subtree → right subtree), letting each node compute its answer from the answers its children return back up the call stack. The trigger: any problem asking about depth, height, path sums, or aggregating information along root-to-leaf paths — when a node's result depends on what lies below it, do post-order DFS and combine child results. Canonical example: Maximum Depth of a Binary Tree, wheredepth(node) = 1 + max(depth(left), depth(right)) and an empty child contributes depth 0.✨ Added by the guide to build intuition — not from the source course.
🏗️ Visual walkthrough — trace it step by step
3 <- root
/ \
9 20
/ \
15 7
We compute maxDepth(root). The rule is depth(node) = 1 + max(depth(node.left), depth(node.right)), and depth(null) = 0. DFS dives to the bottom first, then each node folds its children's depths upward — this is safe because a node's height is fully determined by its subtrees, which are resolved before the node returns.
3
/ \
[9] 20 call stack: 3 -> 9
/ \
15 7
At 9: left=null, right=null
We recurse into 3.left = 9. Node 9 has no children, so it calls depth(null) on both sides. Reaching a leaf is the base-case frontier; the recursion must bottom out here so values can start flowing back up.
3
/ \
(9)=1 20 9 returns 1
/ \
15 7
depth(9) = 1 + max(depth(null), depth(null))
= 1 + max(0, 0) = 1
Both null children return 0, so node 9 returns 1 + max(0,0) = 1. This is correct: a single-node subtree has depth 1. Node 9's frame pops off the stack; control returns to node 3, which now holds leftDepth = 1.
3
/ \
(9)=1 [20] stack: 3 -> 20 -> 15
/ \
(15)=1 7
depth(15) = 1 + max(0, 0) = 1 -> 15 returns 1
Now node 3 explores its right child 20, which stays on the stack (shown as [20]) while we recurse left-first into 20.left = 15. Leaf 15 has two null children, so it returns 1 + max(0,0) = 1 and pops. We always finish the left subtree completely before touching the right — that ordering is what makes the depths we combine final, never partial. Node 20 now holds leftDepth = 1 and still awaits its right child 7.
3
/ \
(9)=1 (20)=2 20 returns 2
/ \
(15)=1 (7)=1
depth(20) = 1 + max(depth(15), depth(7))
= 1 + max(1, 1) = 2
Node 20 recurses into its right child 7, another leaf, which returns 1. Node 20 now has both child results in hand (leftDepth = 1, rightDepth = 1) and returns 1 + max(1,1) = 2. It is safe to combine here because 20 only resumes after both 15 and 7 have fully popped — their returned depths can never change.
3 ROOT resolves
/ \
(9)=1 (20)=2
/ \
(15)=1 (7)=1
depth(3) = 1 + max(depth(9), depth(20))
= 1 + max(1, 2) = 3
The root finally has leftDepth = 1 (from 9) and rightDepth = 2 (from 20). It returns 1 + max(1,2) = 3. The deeper right branch wins via max, so the answer counts the longest root-to-leaf chain 3 → 20 → 15.
maxDepth(root) = 3
Longest path: 3 -> 20 -> 15 (3 nodes)
3 -> 20 -> 7 (also 3, tie)
Visited each node exactly once:
3, 9, 20, 15, 7 -> 5 nodes
Final answer: 3. Every node was visited once, so time is O(n); space is O(h) for the recursion stack (h = tree height, worst case O(n) for a skewed tree). This same post-order fold generalizes directly to Tree Diameter and Path Sum — only the combine step changes.
🎯 Guided practice
Easy — Maximum Depth (Height) of a Binary Tree. Return the number of nodes on the longest root-to-leaf path (LeetCode's convention: empty tree → 0, single node → 1 — this counts nodes, not edges, so it is one greater than the formal edge-based height).
Step 1 — frame it as bottom-up DFS: a node's depth is "1 (for itself) plus the deeper of its two children." The answer at a node depends only on its subtrees, which is the signal for post-order DFS.
Step 2 — base case: if
node is None, return0.Step 3 — recurse and combine:
return 1 + max(maxDepth(node.left), maxDepth(node.right)).Step 4 — trace a root with only a left child:
maxDepth(leftChild) = 1 + max(0, 0) = 1; the missing right child returns0; somaxDepth(root) = 1 + max(1, 0) = 2. Correct.Complexity: O(n) time (each node visited once), O(h) stack space. This is the canonical "post-order DFS returns a value" template — Tree Diameter, balanced-tree check, and many "compute something over every subtree" problems reuse it verbatim, often returning the height while updating a global best on the side.
Medium — Validate Binary Search Tree. Return true only if for every node, all values in its left subtree are strictly smaller and all values in its right subtree are strictly larger.
Step 1 — spot the trap: a local check (
node.left.val < node.val < node.right.val) is wrong — a node deep in a subtree can still violate a distant ancestor. Each node must obey a range inherited from above.Step 2 — carry bounds: define
valid(node, low, high); the root starts with(-inf, +inf). (Equivalently, an in-order traversal must produce strictly increasing values — a clean alternative worth mentioning.)Step 3 — check and narrow: if
node is Nonereturn true. Ifnot (low < node.val < high)return false. Recurse left with(low, node.val)and right with(node.val, high)— going left tightens the upper bound, going right tightens the lower bound.Step 4 — trace root
5, right child7, and7's left child3. Root sees (-inf, +inf): 5 passes. Right subtree inherits (5, +inf): 7 passes. Node 3 inherits (5, 7) and fails5 < 3→ returns false. A parent-only check would wrongly accept it, since 3 < 7 locally holds.Complexity: O(n) time, O(h) space. The lesson — thread constraints down the recursion — generalizes to many "is this structure valid" tree problems where validity is a global, not local, property.
✨ Added by the guide — work these before the full problem set.
Lessons in this topic
- ○Introduction to Tree
- ○Types of Tree
- ○BST Traversal Techniques
- ○BST Operations
- ○Introduction to Tree
- ○Types of Tree
- ○BST Traversal Techniques
- ○BST Operations
- ○Introduction to Level Order Traversal Pattern
- ○Introduction to Tree Depth Pattern
- ○Introduction to Tree Breadth First Search Pattern
- ○Introduction to Tree Depth First Search Pattern
- ○Introduction to Root to Leaf Path Pattern
- ○Introduction to Leaf Processing Pattern
- ○Introduction to Tree Depth First Search Pattern
- ○Introduction to Tree View Pattern
- ○Introduction to Comparison of Two Trees Pattern
- ○Introduction to Tree Breadth First Search Pattern
- ○Introduction to Serialize and Deserialize Tree Pattern
- ○Introduction to Tree
- ○Types of Tree
- ○BST Traversal Techniques
- ○BST Operations
- ○easy Reverse Level Order Traversal
- ○easy Maximum Depth or Height of Binary Tree
- ○easy Minimum Depth of a Binary Tree
- ○easy Balanced Binary Tree
- ○easy Tree Diameter
- ○easy Maximum Depth of N-ary Tree
- ○easy Level Order Successor
- ○easy Binary Tree Path Sum
- ○easy Binary Tree Paths
- ○easy Sum of Root To Leaf Binary Numbers
- ○easy Sum of Left Leaves
- ○easy Leaf-Similar Trees
- ○easy Maximum Depth (or Height) of Binary Tree
- ○easy Balanced Binary Tree
- ○easy Minimum Difference Between BST Nodes
- ○easy Tree Diameter
- ○easy Range Sum of BST
- ○easy Maximum Depth or Height of Binary Tree
- ○easy Symmetric Tree
- ○easy Subtree of Another Tree
- ○easy Cousins in Binary Tree
- ○medium Find Largest Value in Each Tree Row
- ○medium Maximum Width of Binary Tree
- ○medium Maximum Level Sum of a Binary Tree
- ○medium Zigzag Traversal
- ○medium Even Odd Tree
- ○medium Find maximum depth of subtree
- ○medium Sum of Nodes with Even-Valued Grandparent
- ○medium All Nodes Distance K in Binary Tree
- ○medium Connect Level Order Siblings
- ○medium Connect All Level Order Siblings
- ○medium Count Good Nodes in Binary Tree
- ○medium Path Sum III
- ○medium Validate Binary Search Tree
- ○medium Maximum Difference Between Node and Ancestor
- ○medium Insufficient Nodes in Root to Leaf Paths
- ○medium Smallest String Starting From Leaf
- ○medium Pseudo-Palindromic Paths in a Binary Tree
- ○medium All Paths for a Sum
- ○medium Sum of Path Numbers
- ○medium Path With Given Sequence
- ○medium Count Paths for a Sum
- ○medium Check if all leaves are at same level
- ○medium Delete Leaves With a Given Value
- ○medium Deepest Leaves Sum
- ○medium Right View of a Binary Tree
- ○medium Kth Smallest Element in a BST
- ○medium Top View Of the Binary Tree
- ○medium Path Sum III
- ○medium Closest Binary Search Tree Value
- ○medium Bottom View Of the Binary Tree
- ○medium Count Good Nodes in Binary Tree
- ○medium Merge Two Binary Trees
- ○medium Boundary of Binary Tree
- ○medium Lowest Common Ancestor of a Binary Search Tree
- ○medium Validate Binary Search Tree
- ○medium Mirror Image Trees
- ○medium All Nodes Distance K in Binary Tree
- ○medium Even Odd Tree
- ○medium Isomorphic Trees
- ○medium Right View of a Binary Tree
- ○medium Find Largest Value in Each Tree Row
- ○medium Serialize and Deserialize BST
- ○medium Verify Preorder Serialization of a Binary Tree
- ○medium Create Binary Tree From Descriptions
- ○medium Pseudo-Palindromic Paths in a Binary Tree
- ○medium Amount of Time for Binary Tree to Be Infected
- ○medium Maximum Difference Between Node and Ancestor
- ○medium Kth Smallest Element in a BST
- ○hard N-ary Tree Level Order Traversal
- ○hard Serialize and Deserialize N-ary Tree
- ○hard Path with Maximum Sum