MindForge
MindForge
DashboardHelp & Support
Dashboard
MindForge

Interview Preparation Bootcamp

Course progress

0%
0 of 177 modules complete
Start Here0/1
  • How to Use This Course
Data Structures & Algorithms0/27
  • Arrays and Hashing
  • Two Pointers
  • Sliding Window
  • Sliding Window: Harder Problems
  • Binary Search
  • Stacks
  • Practice Test: Arrays, Pointers, Windows, Search and Stacks
  • Trees: Traversals and Core Patterns
  • Binary Search Trees and Rebuilding a Tree
  • Tries (Prefix Trees)
  • Heaps and Priority Queues
  • Graphs: BFS and DFS
  • Topological Sort and Cycle Detection
  • Union-Find (Disjoint Set Union)
  • Practice Test: Trees, Heaps and Graphs
  • Dynamic Programming Basics
  • Dynamic Programming: Two Dimensions
  • Dynamic Programming: Hard String Matching
  • Backtracking
  • Backtracking: Constraint Satisfaction
  • Greedy Algorithms
  • Interval Problems
  • Bit Manipulation
  • Math and Geometry
  • String Manipulation
  • Pattern Recall and Mock Coding Rounds
  • Practice Test: DP, Backtracking, Greedy and More

System Design

Foundations0/16
  • The System Design Interview Framework
  • Back-of-the-Envelope Estimation
  • Networking and Protocols for Design
  • API Design and Idempotency
  • Load Balancing, Proxies, and Scaling Out
  • Caching and CDNs
  • Choosing a Database: Storage, Indexes and Transactions
  • Replication, Partitioning, and Sharding
  • CAP, PACELC, and Levels of Consistency
  • Messaging, Queues, and Event-Driven Architecture
  • Distributed Transactions and Data Integrity
  • Coordination, Consensus, and Distributed Locks
  • Reliability, Resilience, and Rate Limiting
  • Observability, Security, and Cost
  • System Design Cheat Sheet
  • Practice Test: System Design Foundations
Case Studies0/23
  • Design a URL Shortener
  • Design a Rate Limiter
  • Design a Notification Service
  • Design a Job Queue System
  • Design a Chat App (WhatsApp)
  • Design an Analytics Pipeline
  • Design a Multi-tenant SaaS Architecture
  • Design a Social Media Feed (Twitter/X)
  • Design a Search Autocomplete System
  • Design a File Storage Service (Dropbox)
  • Design Google Docs
  • Design a Code Review System
  • Design Netflix
  • Design YouTube
  • Design Spotify
  • Design Uber/Lyft
  • Design Airbnb
  • Design Ticketmaster (Eventbrite)
  • Design an E-commerce Cart, Checkout, and Catalog
  • Design a Payment System
  • Design a Logging and Monitoring System
  • Mock Design Rounds
  • Practice Test: System Design Case Studies
Low-Level Design0/19
  • The LLD Interview Framework and UML
  • OOP Foundations for Design
  • SOLID Principles
  • Creational Patterns
  • Structural Patterns
  • Behavioral Patterns I — Strategy, Observer, Command, State, Template Method
  • Behavioral Patterns II and Anti-Patterns
  • Concurrency in Low-Level Design
  • Design a Parking Lot
  • Design an Elevator System
  • Design a Movie Ticket Booking System
  • Design an Expense-Sharing App (Splitwise)
  • Design a Vending Machine and an ATM
  • Design a Rate Limiter and a Logging Framework
  • Design a Notification Service and an In-Memory Cache
  • Design Games — Tic-Tac-Toe, Snake and Ladder, Chess
  • Database Design for LLD
  • LLD Cheat Sheet and Recall Drill
  • Practice Test: Low-Level Design

Backend

Python0/5
  • How Python Runs Your Code
  • Functions Deep Dive: Closures, Generators, Context Managers, Scope
  • Classes Deep Dive: Method Types, MRO, Metaclasses
  • Standard Library and Serialization
  • Practice Test: Python
Django0/6
  • Django Request Lifecycle
  • Lab: Django Middleware — Request ID & Response Time
  • Django ORM and Querysets
  • Django's Cache Framework
  • Practice Test: Django
  • Django in Production: Debugging Labs
FastAPI0/5
  • FastAPI, ASGI, and Async
  • FastAPI Dependency Injection
  • FastAPI Background Tasks
  • Lab: FastAPI Async — Concurrent Aggregation
  • Practice Test: FastAPI
Databases (PostgreSQL)0/6
  • Indexing
  • Query Optimization
  • Transactions and Isolation
  • Migrations and Schema Changes
  • Hierarchical Data and Safe Concurrent Updates
  • Practice Test: Databases
Caching, Queues & Distributed Systems0/8
  • Redis Caching
  • Redis Advanced Patterns
  • Celery and Background Workers
  • Kafka and Message Queues
  • Async Pipelines
  • Distributed Systems Basics
  • Distributed Locks
  • Practice Test: Caching, Queues and Distributed Systems
APIs, Security & Deployment0/9
  • REST API Design
  • API Versioning and Error Handling
  • API Security and Authentication
  • WebSockets and Real-Time Systems
  • Docker and Containers
  • File Storage with S3
  • Testing Strategies
  • Mock Backend Round
  • Practice Test: APIs, Security and Deployment
Frontend0/38
  • HTML Document Structure and Semantics
  • CSS Box Model, Flexbox and Grid
  • CSS Architecture and Theming
  • CSS and Rendering Performance
  • Variables, Scope, Hoisting, and Truthiness
  • Functions, this, and Binding
  • Objects and Prototypal Inheritance
  • JavaScript Utility Patterns: Cloning, Caching, and Memory
  • Promises From Scratch
  • The Event Loop and Browser Rendering Pipeline
  • The DOM: Nodes, Events, and Traversal
  • Practice Test: HTML, CSS and JavaScript
  • React Rendering Fundamentals
  • React Hooks Internals
  • React Performance
  • Lab: React — Stateful Counter Component
  • State Management
  • State Machines in React
  • React Custom Hooks and Composition Patterns
  • TypeScript for React
  • React Testing
  • Practice Test: React
  • HTTP and Caching
  • Network Performance and Web Vitals
  • Code Splitting and Bundles
  • Virtualization
  • GraphQL and Data Fetching
  • Web Security
  • WebSockets and Real-time
  • SSR and Next.js
  • Micro-frontends
  • Build Tools and Bundling
  • Accessibility
  • Error Handling
  • Internationalization
  • Animation
  • Rapid Recall and Mock Frontend Rounds
  • Practice Test: Performance, Architecture and Production
Behavioral & Interview Day0/14
  • Resume and Project Walkthroughs
  • The STAR Method and Refining Your Stories
  • Conflict and Difficult Coworkers
  • Failures, Incidents, and Obstacles
  • Leadership, Mentorship, and Ownership
  • Teamwork, Communication, and Feedback
  • Growth and Handling Ambiguity
  • Career Goals, Why This Company, and Negotiation
  • Situational Questions
  • Company Research
  • Mock Behavioral Rounds
  • Interview Day
  • After the Interview
  • Practice Test: Behavioral and Interview Day
Trees: Traversals and Core Patterns
Course contents

Interview Preparation Bootcamp

Course progress

0%
0 of 177 modules complete
Start Here0/1
  • How to Use This Course
Data Structures & Algorithms0/27
  • Arrays and Hashing
  • Two Pointers
  • Sliding Window
  • Sliding Window: Harder Problems
  • Binary Search
  • Stacks
  • Practice Test: Arrays, Pointers, Windows, Search and Stacks
  • Trees: Traversals and Core Patterns
  • Binary Search Trees and Rebuilding a Tree
  • Tries (Prefix Trees)
  • Heaps and Priority Queues
  • Graphs: BFS and DFS
  • Topological Sort and Cycle Detection
  • Union-Find (Disjoint Set Union)
  • Practice Test: Trees, Heaps and Graphs
  • Dynamic Programming Basics
  • Dynamic Programming: Two Dimensions
  • Dynamic Programming: Hard String Matching
  • Backtracking
  • Backtracking: Constraint Satisfaction
  • Greedy Algorithms
  • Interval Problems
  • Bit Manipulation
  • Math and Geometry
  • String Manipulation
  • Pattern Recall and Mock Coding Rounds
  • Practice Test: DP, Backtracking, Greedy and More

System Design

Foundations0/16
  • The System Design Interview Framework
  • Back-of-the-Envelope Estimation
  • Networking and Protocols for Design
  • API Design and Idempotency
  • Load Balancing, Proxies, and Scaling Out
  • Caching and CDNs
  • Choosing a Database: Storage, Indexes and Transactions
  • Replication, Partitioning, and Sharding
  • CAP, PACELC, and Levels of Consistency
  • Messaging, Queues, and Event-Driven Architecture
  • Distributed Transactions and Data Integrity
  • Coordination, Consensus, and Distributed Locks
  • Reliability, Resilience, and Rate Limiting
  • Observability, Security, and Cost
  • System Design Cheat Sheet
  • Practice Test: System Design Foundations
Case Studies0/23
  • Design a URL Shortener
  • Design a Rate Limiter
  • Design a Notification Service
  • Design a Job Queue System
  • Design a Chat App (WhatsApp)
  • Design an Analytics Pipeline
  • Design a Multi-tenant SaaS Architecture
  • Design a Social Media Feed (Twitter/X)
  • Design a Search Autocomplete System
  • Design a File Storage Service (Dropbox)
  • Design Google Docs
  • Design a Code Review System
  • Design Netflix
  • Design YouTube
  • Design Spotify
  • Design Uber/Lyft
  • Design Airbnb
  • Design Ticketmaster (Eventbrite)
  • Design an E-commerce Cart, Checkout, and Catalog
  • Design a Payment System
  • Design a Logging and Monitoring System
  • Mock Design Rounds
  • Practice Test: System Design Case Studies
Low-Level Design0/19
  • The LLD Interview Framework and UML
  • OOP Foundations for Design
  • SOLID Principles
  • Creational Patterns
  • Structural Patterns
  • Behavioral Patterns I — Strategy, Observer, Command, State, Template Method
  • Behavioral Patterns II and Anti-Patterns
  • Concurrency in Low-Level Design
  • Design a Parking Lot
  • Design an Elevator System
  • Design a Movie Ticket Booking System
  • Design an Expense-Sharing App (Splitwise)
  • Design a Vending Machine and an ATM
  • Design a Rate Limiter and a Logging Framework
  • Design a Notification Service and an In-Memory Cache
  • Design Games — Tic-Tac-Toe, Snake and Ladder, Chess
  • Database Design for LLD
  • LLD Cheat Sheet and Recall Drill
  • Practice Test: Low-Level Design

Backend

Python0/5
  • How Python Runs Your Code
  • Functions Deep Dive: Closures, Generators, Context Managers, Scope
  • Classes Deep Dive: Method Types, MRO, Metaclasses
  • Standard Library and Serialization
  • Practice Test: Python
Django0/6
  • Django Request Lifecycle
  • Lab: Django Middleware — Request ID & Response Time
  • Django ORM and Querysets
  • Django's Cache Framework
  • Practice Test: Django
  • Django in Production: Debugging Labs
FastAPI0/5
  • FastAPI, ASGI, and Async
  • FastAPI Dependency Injection
  • FastAPI Background Tasks
  • Lab: FastAPI Async — Concurrent Aggregation
  • Practice Test: FastAPI
Databases (PostgreSQL)0/6
  • Indexing
  • Query Optimization
  • Transactions and Isolation
  • Migrations and Schema Changes
  • Hierarchical Data and Safe Concurrent Updates
  • Practice Test: Databases
Caching, Queues & Distributed Systems0/8
  • Redis Caching
  • Redis Advanced Patterns
  • Celery and Background Workers
  • Kafka and Message Queues
  • Async Pipelines
  • Distributed Systems Basics
  • Distributed Locks
  • Practice Test: Caching, Queues and Distributed Systems
APIs, Security & Deployment0/9
  • REST API Design
  • API Versioning and Error Handling
  • API Security and Authentication
  • WebSockets and Real-Time Systems
  • Docker and Containers
  • File Storage with S3
  • Testing Strategies
  • Mock Backend Round
  • Practice Test: APIs, Security and Deployment
Frontend0/38
  • HTML Document Structure and Semantics
  • CSS Box Model, Flexbox and Grid
  • CSS Architecture and Theming
  • CSS and Rendering Performance
  • Variables, Scope, Hoisting, and Truthiness
  • Functions, this, and Binding
  • Objects and Prototypal Inheritance
  • JavaScript Utility Patterns: Cloning, Caching, and Memory
  • Promises From Scratch
  • The Event Loop and Browser Rendering Pipeline
  • The DOM: Nodes, Events, and Traversal
  • Practice Test: HTML, CSS and JavaScript
  • React Rendering Fundamentals
  • React Hooks Internals
  • React Performance
  • Lab: React — Stateful Counter Component
  • State Management
  • State Machines in React
  • React Custom Hooks and Composition Patterns
  • TypeScript for React
  • React Testing
  • Practice Test: React
  • HTTP and Caching
  • Network Performance and Web Vitals
  • Code Splitting and Bundles
  • Virtualization
  • GraphQL and Data Fetching
  • Web Security
  • WebSockets and Real-time
  • SSR and Next.js
  • Micro-frontends
  • Build Tools and Bundling
  • Accessibility
  • Error Handling
  • Internationalization
  • Animation
  • Rapid Recall and Mock Frontend Rounds
  • Practice Test: Performance, Architecture and Production
Behavioral & Interview Day0/14
  • Resume and Project Walkthroughs
  • The STAR Method and Refining Your Stories
  • Conflict and Difficult Coworkers
  • Failures, Incidents, and Obstacles
  • Leadership, Mentorship, and Ownership
  • Teamwork, Communication, and Feedback
  • Growth and Handling Ambiguity
  • Career Goals, Why This Company, and Negotiation
  • Situational Questions
  • Company Research
  • Mock Behavioral Rounds
  • Interview Day
  • After the Interview
  • Practice Test: Behavioral and Interview Day
Browsing without an account — progress is saved in this browser only.Sign in to save it

Trees: Traversals and Core Patterns

Reading120 min

Picture a company org chart: a CEO at the top, managers below, workers below them. That shape, one node with children, children with their own children, is a tree. Most tree problems have a simple trick: the answer for a node is built from the answers of its children. Write a function that trusts its own recursive calls, and the tree problem nearly solves itself.

Visiting every node in a tree

Take this small tree of numbers:

        5
       / \
      3   8
     / \   \
    1   4   9

There are four common ways to walk through it, and they differ only in when you record a node compared to its children.

  • Preorder (node, left, right): record the node first, then its subtrees. 5, 3, 1, 4, 8, 9. Good for copying or serializing a tree, since you write the parent before you write its children.
  • Inorder (left, node, right): record the node between its subtrees. 1, 3, 4, 5, 8, 9. For a search tree (covered in the next lesson), this always comes out sorted.
  • Postorder (left, right, node): record the node last. 1, 4, 3, 9, 8, 5. Use this whenever children must be fully handled before the parent, such as deleting a tree from the bottom up, or totaling up a subtree's values.
  • Level order: visit row by row, left to right: [5], [3, 8], [1, 4, 9]. This one isn't recursive at all; it uses a queue, the same tool BFS uses on graphs later in this course.
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def preorder(root):
    if root is None:
        return []
    return [root.val] + preorder(root.left) + preorder(root.right)

def inorder(root):
    if root is None:
        return []
    return inorder(root.left) + [root.val] + inorder(root.right)

def postorder(root):
    if root is None:
        return []
    return postorder(root.left) + postorder(root.right) + [root.val]

from collections import deque

def level_order(root):
    if root is None:
        return []
    result = []
    queue = deque([root])
    while queue:
        level = []
        for _ in range(len(queue)):
            node = queue.popleft()
            level.append(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        result.append(level)
    return result

The versions above build small lists and glue them together with +. That reads well, but real code usually passes one shared list down through the recursion instead, so it doesn't keep building and throwing away little lists. Know both; be ready to write the faster one if asked.

Remember: preorder writes the parent first, postorder writes it last, inorder puts it in the middle. Level order is the odd one out: no recursion, just a queue.

Sign in to try this knowledge check.

Doing it without recursion

Every recursive traversal above has an iterative version that uses an explicit stack instead of the call stack. Interviewers ask for this to check that you understand what the recursion was actually doing, not that you memorized three lines.

Preorder is the easiest to convert: push right before left, so left comes off the stack first.

def preorder_iterative(root):
    if root is None:
        return []
    result = []
    stack = [root]
    while stack:
        node = stack.pop()
        result.append(node.val)
        if node.right:
            stack.append(node.right)
        if node.left:
            stack.append(node.left)
    return result

Inorder is trickier, since you can't just push both children at once. You have to walk all the way left first, process the node you land on, then step right:

def inorder_iterative(root):
    result = []
    stack = []
    node = root
    while stack or node:
        while node:          # go as far left as possible
            stack.append(node)
            node = node.left
        node = stack.pop()   # process the node
        result.append(node.val)
        node = node.right    # then explore the right subtree
    return result

Postorder iteratively is the fiddly one. Do a "node, right, left" walk (swap the push order from preorder above), then reverse the result. That flips it into "left, right, node" for free.

def postorder_iterative(root):
    if root is None:
        return []
    result = []
    stack = [root]
    while stack:
        node = stack.pop()
        result.append(node.val)
        if node.left:
            stack.append(node.left)
        if node.right:
            stack.append(node.right)
    return result[::-1]

Remember: preorder-iterative pushes right then left. Postorder-iterative is preorder's "node, right, left" twin, reversed at the end. Inorder needs a "walk left, then pop" loop.

Sign in to try this knowledge check.

Two facts about a tree's shape

Two properties come up again and again, so get them cold.

Height: how many edges are on the longest path from a node down to a leaf. A single node has height 0. Height is naturally computed bottom-up, a postorder-shaped recursion: a node's height is 1 more than the taller of its two children's heights.

Balance: a tree is height-balanced if, at every node, the heights of its left and right subtrees differ by at most 1. Checking this the naive way, calling a height function at every node, costs O(n²) in the worst case on a long, skewed tree, since you recompute overlapping heights over and over. The fast version computes height and checks balance in the same single postorder pass, O(n) total.

Remember: compute height bottom-up. Check balance in the same pass as computing height, don't call a separate height function per node.

Sign in to try this knowledge check.

Invert Binary Tree

Invert Binary Tree (LeetCode 226)

Flip a photo of the tree left to right: every left child becomes a right child and vice versa, all the way down. That is the whole problem. It is a postorder-shaped recursion: invert both subtrees, then swap them onto the node.

Approach: Base case: None inverts to None. Recursive case: swap root.left and root.right, after first inverting each of them.

def invert_tree(root):
    if root is None:
        return None
    root.left, root.right = invert_tree(root.right), invert_tree(root.left)
    return root

Complexity: Time O(n), visits every node once. Space O(h) for the recursion stack, where h is the tree's height (O(log n) if balanced, O(n) if it's a long chain).

Common mistakes:

  • Swapping root.left/root.right before recursing on the original children. That inverts the wrong subtree. The simultaneous-assignment version above sidesteps this by finishing both recursive calls before either assignment happens.
  • Forgetting the None base case, which causes infinite recursion.

Remember: invert the children first, then swap them onto the parent, not the other way around.

Sign in to try this knowledge check.

Maximum Depth of Binary Tree

Maximum Depth of Binary Tree (LeetCode 104)

The depth of a tree is 1 (for the current node) plus whichever of its two subtrees goes deeper. That's the entire recursion, a textbook postorder shape.

Approach: Base case: an empty tree has depth 0. Recursive case: 1 + max(depth(left), depth(right)).

def max_depth(root) -> int:
    if root is None:
        return 0
    return 1 + max(max_depth(root.left), max_depth(root.right))

Complexity: Time O(n), space O(h) for the recursion stack.

Common mistakes:

  • Off-by-one: forgetting the + 1 for the current node, or adding it twice.
  • Writing an iterative BFS version without correctly counting levels; you need to count levels, not nodes.

Remember: depth is 1 plus the deeper child's depth. An empty tree has depth 0.

Sign in to try this knowledge check.

Same Tree

Same Tree (LeetCode 100)

Two trees match if their roots hold the same value and both pairs of subtrees also match. That's a direct structural recursion comparing two trees side by side, step by step.

Approach: Base cases: both None means equal; exactly one None means not equal. Recursive case: values match AND left subtrees match AND right subtrees match.

def is_same_tree(p, q) -> bool:
    if p is None and q is None:
        return True
    if p is None or q is None:
        return False
    return (
        p.val == q.val
        and is_same_tree(p.left, q.left)
        and is_same_tree(p.right, q.right)
    )

Complexity: Time O(min(n, m)), stops the instant the trees diverge. Space O(min(h_p, h_q)) recursion stack.

Common mistakes:

  • Reading p.val before checking whether p is None. That crashes with an attribute error on None.

Remember: check both-None and either-None first. Only then compare values and recurse into both children.

Sign in to try this knowledge check.

Binary Tree Level Order Traversal

Binary Tree Level Order Traversal (LeetCode 102)

"Group nodes by their depth" is exactly what BFS gives you for free, as long as you process the queue one full level at a time instead of one node at a time.

Approach: Use a queue. At each step, note the current queue length; that number is exactly how many nodes belong to the current level. Pop that many, collect their values, and enqueue their children for the next round.

from collections import deque

def level_order_traversal(root):
    if root is None:
        return []
    result = []
    queue = deque([root])
    while queue:
        level_size = len(queue)
        level_values = []
        for _ in range(level_size):
            node = queue.popleft()
            level_values.append(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        result.append(level_values)
    return result

Complexity: Time O(n), every node is enqueued and dequeued exactly once. Space O(w) for the queue, where w is the widest level, plus O(n) for the output.

Common mistakes:

  • Not snapshotting the queue's length before the inner loop. Checking the live length inside the loop lets it grow as you enqueue children, which merges levels together.
  • Using list.pop(0) (O(n) per call) instead of deque.popleft() (O(1)), or using a stack where a queue is needed.

Remember: snapshot the queue length before draining it. That number is exactly one level's worth of nodes.

Sign in to try this knowledge check.

Naming the shape before you code

Every problem in this lesson reduces to one of three shapes: does a node's answer depend only on its children's answers (postorder: Invert, Max Depth)? Does it depend on walking two trees in lockstep (Same Tree)? Or does it need to go level by level (Level Order)? Spend ten seconds naming which shape a new tree problem fits before writing any code. Guessing at the recursion instead of naming the shape first is where most tree bugs start.

Remember: name the shape (postorder, lockstep comparison, or level-by-level) before you write a single line.

Sign in to try this knowledge check.
PreviousPractice Test: Arrays, Pointers, Windows, Search and StacksNextBinary Search Trees and Rebuilding a Tree

Course progress

0%
0 of 177 modules complete

On this page

  • Visiting every node in a tree
  • Doing it without recursion
  • Two facts about a tree's shape
  • Invert Binary Tree
  • Maximum Depth of Binary Tree
  • Same Tree
  • Binary Tree Level Order Traversal
  • Naming the shape before you code