Graph Structures and Fundamental Graph AlgorithmsIntroductionGraphs provide a general model for relationships among entities. A communication network, road system, social network, dependency structure, recommendation system, and workflow can all be represented as graphs. In a graph, entities are represented by vertices and relationships by edges. Unlike linear structures such as arrays and linked lists, graphs express many-to-many relationships and may contain cycles, alternative routes, hierarchical dependencies, and disconnected regions. This chapter develops the principal representations and algorithms used to process graphs. It covers graph traversal, topological ordering, strongly connected components, minimum spanning trees, and shortest paths. The emphasis is not only on implementation, but also on selecting an appropriate algorithm, proving correctness, and evaluating time and space complexity. Learning ObjectivesAfter studying this chapter, you should be able to: - Model directed, undirected, weighted, and unweighted networks.
- Select and implement adjacency-list and adjacency-matrix representations.
- Apply breadth-first search and depth-first search.
- Explain cycle detection and topological sorting.
- Compute strongly connected components in directed graphs.
- Construct minimum spanning trees using Kruskal’s and Prim’s algorithms.
- Select an appropriate shortest-path algorithm according to graph properties.
- Justify algorithmic correctness and analyze complexity.
- Evaluate practical limitations involving scale, negative weights, disconnected graphs, and changing networks.
1. Graph FoundationsA graph is written as G = (V, E), where V is a finite set of vertices and E is a set of edges. An edge may be represented as an unordered pair {u, v} in an undirected graph or an ordered pair (u, v) in a directed graph. Important terminology includes: - Order: the number of vertices, |V|.
- Size: the number of edges, |E|.
- Degree: the number of incident edges at a vertex in an undirected graph.
- Indegree: the number of incoming edges to a vertex.
- Outdegree: the number of outgoing edges from a vertex.
- Path: a sequence of vertices connected by edges.
- Simple path: a path containing no repeated vertex.
- Cycle: a closed path whose first and last vertices are identical.
- Connected graph: an undirected graph in which every pair of vertices has a path between them.
- Strongly connected graph: a directed graph in which every vertex can reach every other vertex.
- Component: a maximal connected subgraph.
- Weight: a numerical cost, distance, time, capacity, or preference assigned to an edge.
A graph is sparse when |E| is close to |V|, and dense when |E| is close to |V|². This distinction strongly influences representation choice. 1.1 Common Graph ClassesAn undirected graph models reciprocal relationships, such as physical links. A directed graph models one-way relationships, such as hyperlinks or prerequisite dependencies. A weighted graph assigns values to edges; an unweighted graph treats every edge as having equal cost. A simple graph contains neither self-loops nor parallel edges, whereas a multigraph may contain multiple edges between the same pair of vertices. A tree is a connected, undirected, acyclic graph. A tree with |V| vertices has exactly |V| − 1 edges. A forest is a collection of disjoint trees. A directed acyclic graph (DAG) is a directed graph containing no directed cycle. 2. Graph Representations2.1 Adjacency MatrixAn adjacency matrix A is a |V| × |V| array. For an unweighted graph, A[u][v] is 1 when an edge exists and 0 otherwise. For a weighted graph, it stores the edge weight, while a special value such as infinity represents no edge. The matrix requires Θ(V2) space and tests whether an edge exists in Θ(1) time. It is effective for dense graphs and algorithms that repeatedly inspect every possible pair of vertices. Its main disadvantage is the cost of scanning all |V| possible neighbors of a vertex, even when few edges exist. 2.2 Adjacency ListAn adjacency list stores, for each vertex, a collection of its neighbors. For a directed weighted graph, each entry may contain a pair (neighbor, weight). The representation requires Θ(V+E) space and is usually the preferred representation for sparse graphs. Iterating over all neighbors takes time proportional to the degree of the vertex. 2.3 Edge ListAn edge list stores edges as records such as (u, v, w). It is particularly useful for Kruskal’s algorithm and for algorithms that repeatedly process every edge. It supports compact storage but is inefficient for finding all neighbors of a specific vertex unless additional indexing is used. | Representation | Space | Edge-existence test | Best use |
|---|
| Adjacency matrix | Θ(V2) | Θ(1) | Dense graphs | | Adjacency list | Θ(V+E) | Usually degree-dependent | Sparse graphs and traversal | | Edge list | Θ(E) | Θ(E) | Edge-oriented processing |
3. Breadth-First and Depth-First SearchGraph traversal systematically visits reachable vertices. A visited structure is essential because graphs may contain cycles. 3.1 Breadth-First SearchBreadth-first search (BFS) explores vertices in layers according to their number of edges from a source. It uses a queue. BFS(G, s):
for each vertex v:
visited[v] ← false
distance[v] ← ∞
visited[s] ← true
distance[s] ← 0
enqueue(Q, s)
while Q is not empty:
u ← dequeue(Q)
for each neighbor v of u:
if visited[v] = false:
visited[v] ← true
distance[v] ← distance[u] + 1
parent[v] ← u
enqueue(Q, v)With adjacency lists, BFS runs in O(V+E) time and O(V) auxiliary space. It computes shortest paths measured by number of edges in an unweighted graph. It does not generally compute minimum-weight paths. Correctness idea. When a vertex is removed from the queue, all vertices at smaller distance have already been processed. Every newly discovered vertex is reached through a path one edge longer than its parent’s path. Therefore, the first assigned distance is the minimum number of edges from the source. 3.2 Depth-First SearchDepth-first search (DFS) follows one path as deeply as possible before backtracking. It can be implemented recursively or with an explicit stack. DFS(G):
for each vertex u:
visited[u] ← false
for each vertex u:
if visited[u] = false:
DFS-VISIT(u)
DFS-VISIT(u):
visited[u] ← true
for each neighbor v of u:
if visited[v] = false:
parent[v] ← u
DFS-VISIT(v)DFS runs in O(V+E) time with adjacency lists. Recursive implementations require stack space proportional to the maximum depth; in production systems, an explicit stack may avoid recursion limits. DFS supports cycle detection, topological sorting, connected-component discovery, articulation-point analysis, and strongly connected-component algorithms. In directed graphs, recording discovery and finishing times helps classify edges. An edge to a currently active ancestor is a back edge and proves the existence of a directed cycle. 4. Topological SortingA topological ordering of a DAG is a linear ordering of its vertices such that for every edge u → v, u appears before v. Such an ordering exists if and only if the graph is acyclic. 4.1 Kahn’s AlgorithmKahn’s algorithm repeatedly removes vertices with indegree zero. TOPOLOGICAL-SORT(G):
compute indegree[v] for every vertex v
place all vertices with indegree 0 in queue Q
ordering ← empty list
while Q is not empty:
u ← dequeue(Q)
append u to ordering
for each edge u → v:
indegree[v] ← indegree[v] − 1
if indegree[v] = 0:
enqueue(Q, v)
if length(ordering) ≠ |V|:
report a directed cycle
else:
return orderingThe algorithm runs in O(V+E) time and uses O(V) additional space. If several zero-indegree vertices are available, different valid orderings may result. A priority queue can produce a deterministic order, at the cost of O((V+E) log V) time. 4.2 DFS-Based OrderingA DFS-based method appends each vertex when its exploration finishes and then reverses the resulting list. This is valid only if no back edge is encountered. Both methods are useful: Kahn’s algorithm makes dependency availability explicit, while DFS integrates naturally with other depth-first analyses. 5. Strongly Connected ComponentsA strongly connected component (SCC) of a directed graph is a maximal set of vertices in which every vertex can reach every other vertex. SCCs condense naturally into a DAG: each component becomes one vertex, and edges between components are retained. 5.1 Kosaraju’s AlgorithmKosaraju’s algorithm uses two DFS phases: - Run DFS on the original graph and record vertices by decreasing finishing time.
- Reverse every edge.
- Process vertices in the recorded order on the reversed graph. Each DFS tree is one SCC.
The running time is O(V+E) because each phase scans vertices and edges a constant number of times. The method is conceptually simple and works well with adjacency lists. 5.2 Tarjan’s AlgorithmTarjan’s algorithm finds all SCCs in one DFS. It maintains discovery indices, low-link values, and a stack. The low-link value of a vertex records the smallest discovery index reachable from that vertex through DFS-tree edges followed by at most one back edge. When a vertex’s low-link value equals its discovery index, it is the root of an SCC. Tarjan’s algorithm also runs in O(V+E) time and uses linear space. Its one-pass design is efficient, although its implementation requires careful stack and low-link management. 6. Minimum Spanning TreesFor a connected, weighted, undirected graph, a spanning tree connects every vertex without cycles. A minimum spanning tree (MST) is a spanning tree with minimum total edge weight. If T is a spanning tree, its weight is: w(T) = ∑e ∈ T w(e) Here, w(e) is the weight of edge e. The formula assumes additive edge costs and an undirected graph. An MST is not a shortest-path tree: it minimizes total network connection cost, not the distance from one chosen source to every destination. 6.1 Kruskal’s AlgorithmKruskal’s algorithm sorts all edges by nondecreasing weight and adds an edge whenever it joins two currently separate components. A disjoint-set union (DSU) structure supports this test using find and union operations. KRUSKAL(G):
T ← empty set
make-set(v) for every vertex v
sort edges by nondecreasing weight
for each edge (u, v):
if find(u) ≠ find(v):
add (u, v) to T
union(u, v)
return TSorting dominates the running time: O(E log E). With path compression and union by rank, DSU operations are almost constant amortized time, formally O(α(V)), where α is the inverse Ackermann function. Kruskal’s algorithm can produce a minimum spanning forest when the graph is disconnected. 6.2 Prim’s AlgorithmPrim’s algorithm begins with one vertex and repeatedly adds the minimum-weight edge connecting the current tree to a vertex outside it. With a binary heap and adjacency lists, its complexity is O(E log V). With an adjacency matrix and a simple array, it is O(V2), which can be preferable for dense graphs. Cut property. For any cut dividing the vertices into two sets, a minimum-weight edge crossing that cut is safe for some MST. Both Kruskal’s and Prim’s algorithms are justified by repeatedly applying this property. If edge weights are distinct, the MST is unique; with equal weights, multiple MSTs may exist, although their total weight is the same. 7. Shortest-Path AlgorithmsA shortest-path problem seeks a path of minimum total weight. For a path P, its length is: w(P) = ∑e ∈ P w(e) Weights must represent additive costs. The appropriate algorithm depends on whether the graph is weighted, whether negative edges exist, and whether one needs a single-source, single-pair, or all-pairs result. 7.1 Dijkstra’s AlgorithmDijkstra’s algorithm solves the single-source shortest-path problem when all edge weights are nonnegative. It maintains a tentative distance and repeatedly selects the unsettled vertex with the smallest tentative value. DIJKSTRA(G, s):
dist[v] ← ∞ for every v
dist[s] ← 0
priority queue ← (0, s)
while priority queue is not empty:
(d, u) ← extract-min()
if d ≠ dist[u]: continue
for each edge u → v with weight w:
if dist[u] + w < dist[v]:
dist[v] ← dist[u] + w
parent[v] ← u
insert (dist[v], v)With a binary heap and adjacency lists, the common implementation runs in O((V+E) log V), often written as O(E log V) for connected graphs. It is invalid when negative-weight edges are present because a vertex considered final may later be improved. 7.2 Bellman–Ford AlgorithmBellman–Ford supports negative edge weights and detects reachable negative cycles. It relaxes every edge repeatedly for |V| − 1 passes. A final additional pass identifies an improvement, proving a reachable negative cycle. Its complexity is O(VE). Although slower than Dijkstra’s algorithm, it is appropriate when negative costs represent rebates, credits, or currency-exchange effects. A negative cycle makes the shortest-path value undefined because the cycle can be repeated indefinitely to reduce total cost. 7.3 Floyd–Warshall AlgorithmFloyd–Warshall computes all-pairs shortest paths using dynamic programming. Let Dᵏ[i][j] be the shortest distance from i to j using only the first k intermediate vertices. The recurrence is: Dk[i][j] = min(Dk−1[i][j], Dk−1[i][k] + Dk−1[k][j]) The method assumes additive edge weights and permits negative edges, but not negative cycles. It runs in O(V3) time and O(V2) space. A negative diagonal entry after completion indicates a negative cycle. 7.4 Algorithm Selection| Requirement | Suitable algorithm | Main condition |
|---|
| Unweighted single source | BFS | Equal edge costs | | Weighted single source | Dijkstra | No negative edge weights | | Negative edges, single source | Bellman–Ford | Negative cycles must be detected or absent | | All pairs, moderate graph | Floyd–Warshall | No negative cycles | | Minimum total connection cost | Prim or Kruskal | Undirected weighted graph |
8. Worked Example: Network Design and RoutingConsider an undirected network with vertices A, B, C, D, and E and weighted links: AB = 2, AC = 5, BC = 1, BD = 4, CD = 2, CE = 3, and DE = 1. 8.1 Minimum Spanning TreeKruskal’s algorithm sorts the edges: BC(1), DE(1), AB(2), CD(2), CE(3), BD(4), AC(5). Add BC, then DE, then AB. At this point, {A, B, C} and {D, E} are separate. Add CD, which connects the two components. Four edges have been selected for five vertices, so the process stops. The MST is {BC, DE, AB, CD}, with total weight 1 + 1 + 2 + 2 = 6. 8.2 Shortest Paths from AUsing Dijkstra’s algorithm: - Initial distances: A = 0; B = 2; C = 5; D = ∞; E = ∞.
- Settle B. Through B, C improves to 3 and D becomes 6.
- Settle C. Through C, D improves to 5 and E becomes 6.
- Settle D. Through D, E improves to 6, equal to its current value.
- Settle E.
Therefore, shortest distances from A are A = 0, B = 2, C = 3, D = 5, and E = 6. Notice that the MST and shortest-path tree are different objectives: the MST has total weight 6, while the shortest route from A to E has length 6 through A–B–C–E or A–B–C–D–E. 9. Applied Case: Cloud-Service Dependency and Network PlanningBackground. A cloud platform contains services for authentication, billing, storage, analytics, and notification. Some services depend on others, while data centers are connected by links with installation costs and latency. Problem. The engineering team must determine a safe deployment order, identify circular dependencies, design a low-cost redundant backbone, and route requests with minimum latency. Analysis. Service dependencies are modeled as a directed graph. Kahn’s algorithm produces a deployment order if the graph is acyclic; failure to process all services identifies a dependency cycle requiring architectural review. SCC analysis groups mutually dependent services, revealing tightly coupled modules that may need to be deployed as a unit or refactored. Data-center links form a weighted undirected graph. Kruskal’s algorithm produces a minimum-cost backbone, although operational requirements may require additional redundant links beyond the MST. Request routing uses Dijkstra’s algorithm when latency values are nonnegative. If the platform models credits or adjustments as negative weights, Bellman–Ford is required, together with explicit negative-cycle safeguards. Outcome. The organization obtains four distinct analytical products: a valid deployment sequence, a set of dependency cycles or SCCs, a minimum-cost connecting backbone, and source-to-destination routes. These outputs should not be conflated: a dependency order is not a routing path, and an MST is not a set of minimum-delay routes. Learning points. Correct modeling precedes algorithm selection. Network objectives must be stated precisely, and operational constraints such as resilience, capacity, security zones, and changing edge weights may require extensions beyond textbook algorithms. 10. Correctness, Complexity, and Engineering PracticeA proof of correctness should connect an algorithm’s invariant to its output. BFS maintains layer distances; Kahn’s algorithm maintains that every removed vertex has no remaining predecessor; Kruskal’s algorithm maintains an acyclic forest and uses safe crossing edges; Dijkstra’s algorithm relies on nonnegative weights to justify finalization. Complexity should distinguish input representation from algorithmic logic. A matrix may make edge lookup constant time but force traversal to inspect V possible neighbors. A list reduces storage for sparse graphs but may require more work for direct edge queries. Space analysis should include auxiliary arrays, queues, stacks, priority queues, and recursion depth. Best practices include validating vertex identifiers, explicitly handling disconnected graphs, documenting weight assumptions, detecting overflow in large distance calculations, using immutable edge records where possible, and testing empty, singleton, cyclic, disconnected, duplicate-edge, and negative-weight cases. For production systems, deterministic tie-breaking improves reproducibility, while instrumentation can expose unexpectedly dense regions or pathological degree distributions. 11. Limitations and Contemporary DirectionsClassical graph algorithms assume a static graph and exact edge data. Real networks may evolve continuously, contain uncertain weights, or impose multiple constraints such as latency, cost, capacity, and reliability. An MST may be inadequate when resilience requires two or more independent paths. Dijkstra’s algorithm may be insufficient when travel times depend on departure time or congestion. Large-scale systems use parallel graph processing, compressed sparse representations, external-memory algorithms, and incremental methods that update results after local changes. Graph databases provide query languages for traversals and pattern matching. Machine-learning systems use graph neural networks for prediction, but learned methods do not replace algorithmic guarantees: a model may estimate link risk, while a classical optimization algorithm enforces connectivity or resource constraints. Graph analytics also raises governance concerns. Social and communication graphs may expose sensitive relationships. Access controls, anonymization, retention limits, and audit trails are necessary. Biased or incomplete graph data can produce unfair recommendations or discriminatory risk scores. Engineers must therefore document data provenance, uncertainty, and the consequences of algorithmic decisions. Chapter SummaryGraphs model relationships among entities through vertices and edges. Adjacency lists are generally efficient for sparse graphs, while matrices suit dense graphs and constant-time edge tests. BFS explores by distance layers and solves unweighted shortest paths; DFS supports cycle detection, ordering, and structural analysis. Topological sorting applies only to DAGs, and SCC algorithms identify mutually reachable regions of directed graphs. Kruskal’s and Prim’s algorithms construct minimum spanning trees for connected weighted undirected graphs, using the cut property for correctness. Dijkstra’s algorithm requires nonnegative edge weights, Bellman–Ford handles negative edges and detects reachable negative cycles, and Floyd–Warshall solves all-pairs problems in cubic time. Effective professional practice requires matching the algorithm to graph properties, proving relevant invariants, analyzing representation-dependent complexity, and addressing dynamic, ethical, and operational constraints. Key TermsGraph: A structure consisting of vertices and edges. Vertex: An entity or node in a graph. Edge: A relationship connecting two vertices. Directed graph: A graph whose edges have orientation. Weighted graph: A graph whose edges carry numerical values. Adjacency matrix: A two-dimensional representation recording edge relationships. Adjacency list: A representation listing each vertex’s neighbors. Breadth-first search: A queue-based layer-by-layer traversal. Depth-first search: A traversal that explores deeply before backtracking. Topological ordering: A dependency-respecting linear order of a DAG. Strongly connected component: A maximal mutually reachable subgraph of a directed graph. Minimum spanning tree: A minimum-weight tree connecting all vertices of a connected undirected graph. Disjoint-set union: A structure supporting dynamic component membership. Shortest path: A minimum-weight route between vertices. Relaxation: An operation that improves a tentative shortest-path distance. Negative cycle: A cycle whose total weight is negative. Sparse graph: A graph with relatively few edges. Dense graph: A graph with a number of edges approaching the maximum possible. Cut property: The principle that a lightest edge crossing a cut is safe for an MST. Review QuestionsShort Answer Questions- What is the principal difference between an adjacency matrix and an adjacency list?
- Why does BFS compute shortest paths in an unweighted graph?
- Under what condition does a topological ordering exist?
- What does a strongly connected component represent?
- Why can Dijkstra’s algorithm fail with negative edge weights?
Long Answer Questions- Explain BFS and DFS, including their data structures, uses, and complexities.
- Compare Kruskal’s and Prim’s algorithms in terms of strategy, data structures, and graph suitability.
- Explain Kosaraju’s algorithm and justify its linear running time.
- Compare Dijkstra, Bellman–Ford, and Floyd–Warshall for shortest-path problems.
- Discuss how graph representation affects both memory consumption and algorithmic performance.
Analytical / Application Questions- Design an algorithmic workflow for deploying services represented by a dependency graph. Explain how cycles and SCCs should be handled.
- A network contains negative edge weights but no known negative cycles. Select an appropriate shortest-path algorithm and justify your choice.
- Explain why a minimum spanning tree should not automatically be used as the organization’s routing structure. Provide a network-planning example.
Multiple Choice Questions with AnswersQuestion 1Which representation requires Θ(V²) space regardless of the number of edges? A. Adjacency matrix B. Adjacency list C. Edge list D. Disjoint-set forest Correct Answer: A. Adjacency matrix Question 2Which algorithm computes shortest paths by number of edges in an unweighted graph? A. Prim’s algorithm B. Breadth-first search C. Kruskal’s algorithm D. Floyd–Warshall Correct Answer: B. Breadth-first search Question 3A topological ordering exists exactly when a directed graph is: A. Complete B. Weighted C. Acyclic D. Undirected Correct Answer: C. Acyclic Question 4What is the principal purpose of disjoint-set union in Kruskal’s algorithm? A. To calculate indegrees B. To detect whether an edge joins different components C. To store shortest distances D. To reverse graph edges Correct Answer: B. To detect whether an edge joins different components Question 5Which condition is required by Dijkstra’s algorithm? A. All edge weights are nonnegative B. The graph must be acyclic C. The graph must be complete D. All vertices must have equal degree Correct Answer: A. All edge weights are nonnegative Question 6For a graph with five vertices, how many edges does any spanning tree contain? A. 4 B. 5 C. 10 D. 25 Correct Answer: A. 4 Question 7Which algorithm can detect a reachable negative-weight cycle? A. BFS B. Prim’s algorithm C. Bellman–Ford D. Topological sorting only Correct Answer: C. Bellman–Ford Question 8What is the standard adjacency-list complexity of BFS? A. Θ(V²) B. Θ(V + E) C. Θ(E²) D. Θ(log V) Correct Answer: B. Θ(V + E) Question 9In the worked network, what was the total weight of the minimum spanning tree? A. 4 B. 5 C. 6 D. 8 Correct Answer: C. 6 Question 10Which algorithm is designed for all-pairs shortest paths with Θ(V³) time? A. Floyd–Warshall B. Kruskal C. BFS D. Prim Correct Answer: A. Floyd–Warshall
|