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
Topological Sort and Cycle Detection
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

Topological Sort and Cycle Detection

Reading150 min

Before you can get dressed, you put on socks before shoes. Before a build system compiles your project, it compiles the files you depend on first. Topological sort answers exactly this question in code: "in what order must these tasks run, given their dependencies?" It only makes sense for graphs with no cycles, so cycle detection and topological sort are really two names for the same idea.

What makes an order possible at all

A DAG (Directed Acyclic Graph) is a directed graph with no cycles: you can never start at a node, follow edges, and arrive back where you started. That property is exactly what makes a valid ordering possible. If task A must come before task B (an edge A→B), a cycle A→B→A would make "before" meaningless, since A would need to come before B and after B at the same time. No valid order could exist.

Topological order: a line-up of nodes such that for every directed edge u → v, u appears before v in the line. A DAG can have more than one valid topological order, or exactly one if it happens to be a strict chain.

# Example: 5 depends on 2 and 0; 4 depends on 0 and 1; 3 depends on 1
# edges: 5->2, 5->0, 4->0, 4->1, 2->3, 3->1
# Valid orders include: [5, 4, 2, 3, 1, 0] and [4, 5, 2, 3, 1, 0]

Key fact: a graph has a valid topological order if and only if it is a DAG. So "can this be topologically sorted?" and "does this graph have a cycle?" are the same question asked two ways, which is why cycle detection and topological sort share one algorithm.

Remember: a topological order exists only if the graph has no cycles. Asking "is it a DAG?" and "can it be sorted?" are the same question.

Sign in to try this knowledge check.

Kahn's algorithm: peel off nodes with nothing left owed to them

Kahn's algorithm repeatedly removes nodes with in-degree 0, meaning no remaining dependencies point at them. That's the whole idea: a node with in-degree 0 can safely run right now, since nothing is still waiting to happen before it.

from collections import deque, defaultdict

def kahn_topo_sort(num_nodes: int, edges: list[tuple[int, int]]) -> list[int]:
    graph = defaultdict(list)
    in_degree = [0] * num_nodes
    for u, v in edges:          # u must come before v
        graph[u].append(v)
        in_degree[v] += 1

    queue = deque(n for n in range(num_nodes) if in_degree[n] == 0)
    order = []

    while queue:
        node = queue.popleft()
        order.append(node)
        for neighbor in graph[node]:
            in_degree[neighbor] -= 1
            if in_degree[neighbor] == 0:
                queue.append(neighbor)

    if len(order) != num_nodes:
        return []  # cycle detected — not all nodes could be processed
    return order

Complexity: Time O(V + E), space O(V + E).

Why it doubles as cycle detection: if the graph has a cycle, the nodes inside that cycle never reach in-degree 0, since each one depends on another node that's also stuck in the cycle. So order ends up shorter than num_nodes. That length check IS the cycle check; no separate logic needed.

Remember: repeatedly peel off nodes with in-degree 0. If the final order is shorter than the node count, a cycle ate the rest.

Sign in to try this knowledge check.

The DFS alternative

Run DFS from every unvisited node, and when a node's DFS call finishes, meaning every one of its descendants is fully processed, push that node onto a stack. Reverse the stack at the end.

def dfs_topo_sort(num_nodes: int, edges: list[tuple[int, int]]) -> list[int]:
    graph = defaultdict(list)
    for u, v in edges:
        graph[u].append(v)

    visited = set()
    stack = []

    def dfs(node):
        visited.add(node)
        for neighbor in graph[node]:
            if neighbor not in visited:
                dfs(neighbor)
        stack.append(node)  # postorder: node goes on stack after all descendants

    for node in range(num_nodes):
        if node not in visited:
            dfs(node)

    return stack[::-1]

Why postorder + reverse works: a node is only pushed after everything it depends on downstream has already been pushed. Reversing puts dependencies before the things that depend on them. This variant needs a separate 3-color cycle check, covered next, because a plain visited set alone can't tell "currently on this DFS path" apart from "already fully processed."

Trade-off: Kahn's is iterative, so it carries no recursion-depth risk, and it detects cycles for free via the length check. DFS-based is often shorter to write when you already have DFS cycle detection in place, and it fits naturally when the problem is phrased as "process a node only after its dependents," as in course-scheduling frameworks.

Remember: DFS-based topo sort pushes a node onto a stack only after all its descendants are done, then reverses. That reversal is the whole trick.

Sign in to try this knowledge check.

Detecting a cycle correctly

For an undirected graph, a plain visited set is enough. If you reach an already-visited neighbor that isn't your immediate parent, that's a cycle.

For a directed graph, the interview-relevant case (Course Schedule and friends), you need three states, not two, because a node can be "visited" from a completed branch without being part of any cycle:

WHITE, GRAY, BLACK = 0, 1, 2  # unvisited, in-progress (on current DFS path), done

def has_cycle_directed(num_nodes: int, edges: list[tuple[int, int]]) -> bool:
    graph = defaultdict(list)
    for u, v in edges:
        graph[u].append(v)

    color = [WHITE] * num_nodes

    def dfs(node):
        color[node] = GRAY
        for neighbor in graph[node]:
            if color[neighbor] == GRAY:
                return True                  # back edge -> cycle
            if color[neighbor] == WHITE and dfs(neighbor):
                return True
        color[node] = BLACK
        return False

    return any(color[n] == WHITE and dfs(n) for n in range(num_nodes))

Pitfall: using a single visited set (two states) on a directed graph gives false positives. Two different branches can both reach the same node without a cycle existing at all, since directed edges don't imply "coming back." The GRAY state, tracking the current recursion path specifically, is what correctly identifies a true back edge.

Remember: directed-graph cycle detection needs three states (white/gray/black), not two. GRAY means "on my current path right now," which is the only thing a true cycle can hit.

Sign in to try this knowledge check.

Course Schedule

LeetCode 207, Topological sort, Detect cycle

"Can you finish all courses?" is exactly "is this prerequisite graph a DAG?" Build the graph, run Kahn's algorithm, and check whether every course got processed.

Approach: Build the adjacency list and in-degree array from the prerequisites, then run Kahn's algorithm; compare the processed count to numCourses.

def canFinish(numCourses: int, prerequisites: list[list[int]]) -> bool:
    graph = defaultdict(list)
    in_degree = [0] * numCourses
    for course, prereq in prerequisites:
        graph[prereq].append(course)
        in_degree[course] += 1

    queue = deque(c for c in range(numCourses) if in_degree[c] == 0)
    processed = 0

    while queue:
        node = queue.popleft()
        processed += 1
        for neighbor in graph[node]:
            in_degree[neighbor] -= 1
            if in_degree[neighbor] == 0:
                queue.append(neighbor)

    return processed == numCourses

Complexity: Time O(V + E), space O(V + E).

Common mistakes: Flipping the edge direction; it runs prereq to course, not course to prereq. Forgetting that courses with zero prerequisites and zero dependents still count toward processed.

Remember: this is topological sort wearing a boolean costume. Just check whether Kahn's algorithm processed every course.

Sign in to try this knowledge check.

Course Schedule II

LeetCode 210, Topological sort, Return order

Identical to Course Schedule I, but instead of a true/false answer, you return the actual valid order (or an empty list if it's impossible).

Approach: Reuse Kahn's algorithm verbatim, but collect the order instead of only counting it.

def findOrder(numCourses: int, prerequisites: list[list[int]]) -> list[int]:
    graph = defaultdict(list)
    in_degree = [0] * numCourses
    for course, prereq in prerequisites:
        graph[prereq].append(course)
        in_degree[course] += 1

    queue = deque(c for c in range(numCourses) if in_degree[c] == 0)
    order = []

    while queue:
        node = queue.popleft()
        order.append(node)
        for neighbor in graph[node]:
            in_degree[neighbor] -= 1
            if in_degree[neighbor] == 0:
                queue.append(neighbor)

    return order if len(order) == numCourses else []

Complexity: Time O(V + E), space O(V + E).

Common mistakes: Returning a partial order instead of an empty list when a cycle exists. Always check the length before returning.

Remember: same algorithm as Course Schedule I, just return the collected order instead of a boolean.

Sign in to try this knowledge check.

Alien Dictionary

LeetCode 269, Topological sort, Hard

You're given a list of words already sorted according to some unknown alien alphabet. Comparing adjacent words reveals relative character ordering: the first character where two consecutive words differ tells you "this letter comes before that letter." Build a graph of those constraints and topologically sort the alphabet's letters.

Approach: For each pair of adjacent words, find the first index where the characters differ; that gives one directed edge word1[i] -> word2[i]. Special case: if word1 is longer than word2 but word2 is exactly its prefix, such as "abc" before "ab", the order is invalid and you return "". Then run Kahn's algorithm over the 26 letters.

def alienOrder(words: list[str]) -> str:
    graph = defaultdict(set)
    in_degree = {c: 0 for word in words for c in word}

    for w1, w2 in zip(words, words[1:]):
        min_len = min(len(w1), len(w2))
        if len(w1) > len(w2) and w1[:min_len] == w2[:min_len]:
            return ""  # invalid: longer word can't be a prefix of the next
        for c1, c2 in zip(w1, w2):
            if c1 != c2:
                if c2 not in graph[c1]:
                    graph[c1].add(c2)
                    in_degree[c2] += 1
                break  # only the first differing pair gives a constraint

    queue = deque(c for c in in_degree if in_degree[c] == 0)
    order = []

    while queue:
        c = queue.popleft()
        order.append(c)
        for neighbor in graph[c]:
            in_degree[neighbor] -= 1
            if in_degree[neighbor] == 0:
                queue.append(neighbor)

    return "".join(order) if len(order) == len(in_degree) else ""

Complexity: Time O(C), where C is the total character count across all words: each adjacent-pair comparison is bounded by word length, and the topological sort itself is O(26) for nodes and edges. Space is O(1) in practice, since there are at most 26 letters.

Common mistakes: Not handling the "prefix but longer" invalid case. Adding duplicate edges when the same letter pair appears across multiple word comparisons; the "already in graph" guard prevents inflating in-degree counts. Continuing the inner loop past the first differing character, since later differences carry no information once the first one is found.

Remember: compare only adjacent words, take only the FIRST differing character per pair, and watch for the "longer word is a prefix of the shorter one" invalid case.

Sign in to try this knowledge check.

Longest Increasing Path in a Matrix

LeetCode 329, DFS + Memoization

Treat each cell as a graph node with a directed edge to any 4-directional neighbor holding a strictly greater value. Since values strictly increase along any path, this graph is automatically acyclic, no separate cycle check needed. Find the longest path in this implicit DAG with DFS plus memoization; it's topological-DAG-style DP even though you never build an explicit adjacency list.

Approach: DFS from each cell, caching "longest increasing path starting here" so overlapping subproblems are never recomputed.

def longestIncreasingPath(matrix: list[list[int]]) -> int:
    if not matrix:
        return 0
    rows, cols = len(matrix), len(matrix[0])
    memo = {}

    def dfs(r, c):
        if (r, c) in memo:
            return memo[(r, c)]
        best = 1
        for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)):
            nr, nc = r + dr, c + dc
            if 0 <= nr < rows and 0 <= nc < cols and matrix[nr][nc] > matrix[r][c]:
                best = max(best, 1 + dfs(nr, nc))
        memo[(r, c)] = best
        return best

    return max(dfs(r, c) for r in range(rows) for c in range(cols))

Complexity: Time O(rows × cols), since memoization computes each cell's answer exactly once. Space O(rows × cols) for the memo and recursion stack.

Common mistakes: Trying to solve this with a visited set instead of memoization. A visited set assumes you never revisit a cell in any path, but here you legitimately want to revisit cells from different starting points. "Longest path starting at (r, c)," cached, is the correct DP framing.

Remember: strictly increasing values make the implicit graph automatically acyclic. Cache "best path starting here" instead of using a visited set.

Sign in to try this knowledge check.
PreviousGraphs: BFS and DFSNextUnion-Find (Disjoint Set Union)

Course progress

0%
0 of 177 modules complete

On this page

  • What makes an order possible at all
  • Kahn's algorithm: peel off nodes with nothing left owed to them
  • The DFS alternative
  • Detecting a cycle correctly
  • Course Schedule
  • Course Schedule II
  • Alien Dictionary
  • Longest Increasing Path in a Matrix