Level Up Your Coding Skills & Crack Interviews — Save up to 50% or more on Educative.io Today! Claim Discount

Arrow
Table of contents

Graph

Graphs are one of the most important concepts to master for coding interviews because they model relationships. Many LeetCode problems are secretly graph problems even when the input looks like a grid, a list of words, or a set of dependencies. Once you learn how to represent graphs and traverse them safely, a large class of hard-looking problems becomes much more predictable.

In simple terms, a graph is a set of nodes connected by edges. Unlike trees, graphs are not necessarily hierarchical, and they may contain cycles, multiple paths, and disconnected groups. That flexibility is exactly why graphs show up so often in interview settings.

What is a graph?

A graph is a non-linear data structure that represents relationships between objects. Formally, a graph is defined as an ordered pair G = (V, E), where:

  • V is a set of vertices (also called nodes).
  • E is a set of edges that connect pairs of vertices.

Each vertex represents an entity (for example, a person, city, or web page). Each edge represents a relationship between two entities (for example, friendship, a road, or a hyperlink).

Depending on the problem, edges may also include additional information such as a weight (cost, distance, or time), and they may be directed or undirected.

Graph vs. Tree

The table below summarizes the most important differences between graphs and trees.

AspectTreeGraph
CyclesA tree never contains a cycle.A graph may contain cycles.
ConnectivityA tree is always connected. Every node is reachable from the root.A graph may be disconnected and can contain multiple connected components.
PathsThere is exactly one unique path between any two nodes.There may be multiple paths between two nodes, or no path at all.
Root and parent relationshipsA tree has a designated root. Every node (except the root) has exactly one parent.A general graph has no required root, and nodes do not follow parent-child constraints.

Real-world intuition

Graphs are easier to understand when you connect them to real-world systems that consist of entities and relationships:

  • Maps and navigation: Cities (or intersections) are nodes, and roads are edges.
  • Social networks: Users are nodes, and friendships relationships form edges.
  • The web: Web pages are nodes, and hyperlinks between pages are directed edges.
  • Airline networks: Airports are nodes, and flights are edges.

In short, a graph is a natural model for any problem that involves a set of items and the connections between them.

Types of graphs

In coding interviews, most graphs can be classified using a few core properties:

Undirected graphs

In an undirected graph, edges have no direction, so the connection works both ways (A — B). This model is common in scenarios such as mutual friendships or two-way roads.

Directed graphs

In a directed graph, each edge has a direction (A → B), meaning the relationship goes one way. This is used for dependencies, prerequisites, or one-way routes.

Weighted graphs

In a weighted graph, each edge carries a numerical value called a weight, such as distance, cost, or travel time. Weighted graphs can be either directed or undirected, depending on whether the relationship is one-way or two-way. They are used when you need the minimum-cost route rather than the fewest steps.

Unweighted graphs

In an unweighted graph, all edges are treated as having the same cost (typically cost = 1). Unweighted graphs can also be directed or undirected, depending on whether edges have direction. These graphs are common when the problem asks for the minimum number of moves or steps.

Cyclic graphs

A cyclic graph contains at least one cycle, meaning there exists a path that starts and ends at the same node.

Acyclic graphs

An acyclic graph contains no cycles. A particularly important case is a Directed Acyclic Graph (DAG), which is a directed graph with no cycles and is common in dependency problems.

Graph terminology for coding interviews

Coding interview questions rely on standard graph terminology. The following definitions are used consistently across most problems:

  • Vertex (node): A single entity in the graph.
  • Edge: A connection between two vertices.
  • Neighbor (adjacent vertex): A vertex that is directly connected to another vertex by an edge.
  • Degree: The number of edges incident to a vertex (i.e., the number of connections it has).
    • In a directed graph, degree is split into:
      • Indegree: The number of incoming edges to a vertex.
      • Outdegree: The number of outgoing edges from a vertex.
  • Path: A sequence of vertices where each consecutive pair is connected by an edge.
  • Cycle: A path that starts and ends at the same vertex, with at least one edge.
  • Connected component: In an undirected graph, a maximal group of vertices such that every vertex in the group is reachable from every other vertex in the same group.
  • Reachable: A vertex B is reachable from vertex A if there exists at least one path from A to B.

Graph representations in code

Graphs can be represented in multiple ways, and choosing the right one affects both performance and implementation clarity. In interviews, the adjacency list is the most common representation, but adjacency matrices and edge lists appear in specific problem types.

Adjacency list

An adjacency list stores, for each node, a list of the nodes directly connected to it (its neighbors). This representation is memory-efficient for sparse graphs, which is the typical case in interview problems.

Python implementation for adjacency list (unweighted)

Python implementation for adjacency list (weighted)

Adjacency matrix

An adjacency matrix is a V × V table where matrix[u][v] indicates whether an edge exists between u and v. In a weighted graph, the cell often stores the edge weight (or a sentinel like infinity if no edge exists).

Python implementation for adjacency matrix (unweighted)

Python implementation for adjacency matrix (weighted)

Edge list

An edge list stores the graph as a list of edges. Each entry is typically (u, v) for an unweighted graph or (u, v, w) for a weighted graph.

Python implementation for edge list

Graph traversal techniques

Most graph problems are solved by traversing the graph: starting from a node and systematically visiting all nodes that are reachable from it. The two fundamental traversal strategies are DFS and BFS.

Depth-first search (DFS)

Depth-first search (DFS) explores as far as possible along one path before backtracking to explore other branches. DFS is a natural fit when you want to fully explore a region or detect cycles.

Python template (iterative DFS)

Time complexity: O(V + E) Space complexity: O(V) in the worst case (visited set + recursion stack or explicit stack)

Python template (recursive DFS)

Time complexity: O(V + E) Space complexity: O(V) in the worst case (visited set + recursion stack or explicit stack)

Now, let’s look at the following illustration for the better understanding of DFS.

1 / 11

Breadth-first search (BFS)

Breadth-first search (BFS) explores the graph level by level, visiting all neighbors of the current frontier before moving outward. BFS is the standard tool when the problem asks for the minimum number of steps in an unweighted graph.

Python template (BFS)

Time complexity: O(V + E)

Space complexity: O(V) in the worst case (queue + visited)

Now, let’s look at the following illustration for the better understanding of BFS.

1 / 12

Good to know!

In graph problems, always track visited nodes. Without it, you may loop forever in cycles or waste time revisiting the same nodes. In most cases, mark a node visited as soon as you discover it (when adding it to a queue/stack). The main exception is weighted shortest path problems, where a node may be reached later with a cheaper cost, so “visited” must be handled more carefully.

Important graph algorithms

Beyond BFS and DFS, the following graph algorithms show up frequently in coding interviews. You do not always need to implement every one from scratch, but you should know what each algorithm solves and when to apply it.

AlgorithmDescription
Topological SortProduces an ordering of nodes in a DAG (dependency scheduling). If no ordering exists, the directed graph has a cycle.
Cycle DetectionDetermines whether a graph contains a cycle. Common in prerequisites (directed) and redundant connection (undirected) problems.
Dijkstra’s AlgorithmFinds shortest paths in weighted graphs with non-negative edge weights (minimum cost/time/distance).
Bellman–FordFinds shortest paths with negative edge weights and can detect negative cycles.
Floyd–WarshallComputes shortest paths between all pairs of nodes (usually only when V is small due to O(V³)).
Minimum Spanning Tree (MST)Connects all nodes with minimum total weight in an undirected weighted graph (minimum cost to connect).
Kruskal’s AlgorithmBuilds an MST by sorting edges and adding them if they don’t create a cycle (uses Union-Find).
Prim’s AlgorithmBuilds an MST by growing from a start node using a min-heap, similar to Dijkstra.
Union-Find (DSU)Efficiently tracks connected components under unions. Used for connectivity queries, cycle detection (undirected), and Kruskal’s MST.
Bipartite CheckChecks whether nodes can be split into two groups with no edges inside a group (2-coloring via BFS/DFS).
Strongly Connected Components (SCC)Finds groups in a directed graph where every node can reach every other (Kosaraju/Tarjan). Useful in advanced dependency/condensation problems.
Shortest Path in a DAGComputes shortest paths efficiently using topological order when the graph is acyclic.

Frequently Asked Questions

How do I recognize a graph problem on LeetCode?

Look for relationships or moves: “connected,” “reachable,” “path,” “shortest,” “minimum steps,” “dependencies/prerequisites,” or grids where you can move between cells.

When should I use BFS instead of DFS?

Use BFS when the question asks for the minimum number of steps/edges in an unweighted graph. Use DFS for exploration, components, and many cycle-detection tasks.

Why do we mark nodes as visited?

Graphs can contain cycles and multiple paths to the same node. Without visited tracking, you may loop forever or redo work many times.

How do we handle disconnected graphs?

Iterate over every node and start a traversal whenever you find an unvisited node. This is how you count connected components.

What’s the difference between a directed and undirected graph in practice?

Directed edges represent one-way relationships (A → B), like prerequisites. Undirected edges represent two-way relationships (A — B), like mutual friendship.

How do I detect a cycle in a directed graph?

Use DFS with 3 states (unvisited, visiting, done). If you reach a “visiting” node again, you found a cycle.

When do I need Dijkstra instead of BFS?

Use Dijkstra when edges have different non-negative weights (cost/time/distance). BFS only guarantees shortest paths when all edges have equal weight.

What is a DAG and why does it matter?

A DAG is a directed graph with no cycles. Dependency/order problems on DAGs are solved with topological sort.

Share with others:

Unlock up to 68% off lifetime access to Coding Interview prep with Educative

Getting ready for coding interviews or sharpening your problem-solving skills? Unlock a lifetime discount with comprehensive resources designed to help you master technical interviews.

Data structures and algorithms

Pattern-based problem solving

Mock interview practice

Real-world coding challenges

Coding Interview Logo