Skip to main content

Maximum Depth of Binary Tree – 3 Approaches Explained (DFS, BFS & Stack)

Maximum Depth of Binary Tree (LeetCode 104)

When I started solving tree problems, I had absolutely no idea how to approach them. This was my first tree problem, so I initially watched a YouTube video to understand the intuition. After that, I explored three different ways to solve the problem. In this post, I’ll explain all three approaches in a simple and beginner-friendly manner.


📌 Problem Statement

Given the root of a binary tree, return its maximum depth.

Maximum Depth is the number of nodes along the longest path from the root node down to the farthest leaf node.


🧠 Approach 1: Recursive Depth-First Search (DFS)

This is the most natural way to solve tree problems because trees are recursive by nature. For every node, we calculate the depth of its left and right subtrees and take the maximum.

class Solution:
    def maxDepth(self, root):
        if not root:
            return 0
        return 1 + max(self.maxDepth(root.left),
                       self.maxDepth(root.right))

Explanation:

  • If the node is None, the depth is 0.
  • Otherwise, compute the depth of left and right subtrees.
  • Add 1 for the current node.

This approach is clean, intuitive, and easy to write. However, it relies on recursion, which may cause stack overflow for very deep trees.


🧠 Approach 2: Breadth-First Search (Level Order Traversal)

In this approach, we traverse the tree level by level using a queue. Each iteration of the loop processes one complete level of the tree.

from collections import deque

class Solution:
    def maxDepth(self, root):
        if not root:
            return 0

        level = 0
        q = deque([root])

        while q:
            for _ in range(len(q)):
                node = q.popleft()
                if node.left:
                    q.append(node.left)
                if node.right:
                    q.append(node.right)
            level += 1

        return level

Explanation:

  • The queue stores nodes of the current level.
  • The inner loop processes all nodes at that level.
  • After finishing a level, we increment the depth counter.

This method avoids recursion and works well when dealing with level-based tree problems.


🧠 Approach 3: Iterative DFS Using a Stack

This approach mimics recursion manually using a stack. Each stack entry stores the node along with its current depth.

class Solution:
    def maxDepth(self, root):
        if not root:
            return 0

        level = 0
        stack = [[root, 1]]

        while stack:
            node, current_level = stack.pop()
            level = max(level, current_level)

            if node.left:
                stack.append([node.left, current_level + 1])
            if node.right:
                stack.append([node.right, current_level + 1])

        return level

Explanation:

  • Each stack element keeps track of the node and its depth.
  • We update the maximum depth whenever we visit a node.
  • This avoids recursion while still performing DFS.

This approach is useful when recursion depth might be a concern and gives full control over traversal.


📊 Comparison of All Three Approaches

Approach Technique Data Structure Extra Space Notes
Recursive DFS Depth-First Search Call Stack O(height) Clean and intuitive, but uses recursion
BFS Level Order Traversal Queue O(width) Best for level-based problems
Iterative DFS Depth-First Search Stack O(height) Avoids recursion, interview-friendly

🔗 Source Code

Comments

Popular posts from this blog

LeetCode 88 Explained: Four Approaches, Mistakes, Fixes & the Final Optimal Python Solution

Evolving My Solution to “Merge Sorted Array” A practical, beginner-friendly walkthrough showing four versions of my code (from a naive approach to the optimal in-place two-pointer solution). Includes explanations, complexity and ready-to-paste code. Problem Summary You are given two sorted arrays: nums1 with size m + n (first m are valid) nums2 with size n Goal: Merge nums2 into nums1 in sorted order in-place . Version 1 — Beginner Approach (Extra List) I merged into a new list then copied back. Works, but not in-place and uses extra memory. class Solution: def merge(self, nums1, m, nums2, n): result = [] p1 = 0 p2 = 0 for _ in range(m+n): if p1 >= m: result.extend(nums2[p2:n]) break elif p2 >= n: result.extend(nums1[p1:m]) break elif nu...

Introducing CodeMad: Your Ultimate Universal IDE with Custom Shortcuts

Introducing CodeMad: Your Ultimate Multi-Language IDE with Custom Shortcuts Welcome to the world of CodeMad, your all-in-one Integrated Development Environment (IDE) that simplifies coding and boosts productivity. Developed in Python, CodeMad is designed to make your coding experience smoother and more efficient across a variety of programming languages, including C, C++, Java, Python, and HTML. Whether you're a beginner or an experienced programmer, CodeMad is your go-to tool. In this blog, we'll dive deep into the workings of CodeMad, highlighting its unique features and easy installation process. The Power of Shortcuts CodeMad's intuitive interface is built around a set of powerful keyboard shortcuts that make coding a breeze. Here are some of the key shortcuts you'll find in CodeMad: Copy (Ctrl+C) : Duplicate text with ease. Paste (Ctrl+V) : Quickly insert copied content into your code. Undo (Ctrl+Z) and Redo (Ctrl+Y) : Correct mistakes and s...

Product of Array Except Self in Python | Prefix & Suffix Explained (LeetCode 238)

Problem Overview The Product of Array Except Self is a classic problem that tests your understanding of array traversal and optimization. The task is simple to state but tricky to implement efficiently. Given an integer array nums , you need to return an array such that each element at index i is equal to the product of all the elements in nums except nums[i] . The challenge is that: Division is not allowed The solution must run in O(n) time Initial Thoughts At first glance, it feels natural to compute the total product of the array and divide it by the current element. However, this approach fails because division is forbidden and handling zeroes becomes messy. This pushed me to think differently — instead of excluding the current element, why not multiply everything around it? That’s where the prefix and suffix product pattern comes in. Key Insight: Prefix & Suffix Products For every index i : Prefix product → product of all elements to t...