C++
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C++
-
Section 4: Memory Management
-
Section 5: Templates and Generic Programming
-
Section 6: The Standard Template Library
-
Section 7: Modern C++ Features
-
Section 8: Error Handling
-
Section 9: Multithreading
-
Section 10: Operator Overloading and Type Conversion
-
Section 11: Advanced Topics
-
Section 12: Tooling and Build Systems
-
Section 13: Design Patterns in C++
-
Section 14: Interfacing with C and Systems Programming
-
Section 15: Networking and IPC Basics
-
Section 16: Graphics and Game Programming Basics
-
Section 17: The Boost Libraries
-
Section 18: Data Structures and Algorithms in C++
-
Section 19: Practical Projects
-
Section 20: More Concurrency Patterns
-
Section 21: More OOP and Design Practice
-
Section 22: File I/O and Streams
-
Section 23: More Standard Library
-
Section 24: Practice Exercises
-
Section 25: Interview and Algorithm Practice
-
Section 26: Compiler and Language Internals
-
Section 27: GUI and Application Frameworks Overview
-
Section 28: Testing and Quality Practices
-
Section 29: Numerics Library
-
Section 30: Concepts Library (C++20)
-
Section 31: Ranges Library (C++20) In Depth
-
Section 32: More Utility Library
163: Implementing a Graph with Adjacency Lists in C++
Why bother with adjacency lists instead of just using a 2D array?
When you first look at graphs, a 2D array (an adjacency matrix) seems like the obvious choice. If city A connects to city B, you just set matrix[A][B] = 1. Simple, right? But here is the problem: most real-world graphs are "sparse."
Think about a flight network. There might be 500 airports in the world, but any single airport only flies to a handful of others. In a matrix, you'd be allocating a 500x500 grid—250,000 cells—just to store maybe 2,000 actual flights. You're wasting a massive amount of memory on zeros. With an adjacency list, you only store the connections that actually exist. It turns your space complexity from $O(V^2)$ to $O(V + E)$, where $V$ is vertices and $E$ is edges. Unless your graph is incredibly dense, the list is almost always the way to go.
What's the cleanest way to actually structure this in C++?
You'll see some old textbooks use linked lists, but in modern C++, I almost always use a std::vector of std::vectors. Why? Cache locality. Jumping around memory with std::list is a performance killer. If your nodes are represented by integers (0, 1, 2...), a vector of vectors is incredibly efficient.
#include <iostream>
#include <vector>
class FlightGraph {
int numAirports;
// Each index in the outer vector represents an airport
// The inner vector contains the IDs of airports it connects to
std::vector<std::vector<int>> adjLists;
public:
FlightGraph(int airports) : numAirports(airports) {
adjLists.resize(airports);
}
void addFlight(int source, int destination) {
// For an undirected graph (round trip), add both ways
adjLists[source].push_back(destination);
adjLists[destination].push_back(source);
}
void printNetwork() {
for (int i = 0; i < numAirports; ++i) {
std::cout < "Airport " < i < " connects to: ";
for (int neighbor : adjLists[i]) {
std::cout < neighbor < " ";
}
std::cout < "\n";
}
}
};
Notice I used resize() in the constructor. This ensures we don't have to call push_back on the outer vector, which prevents unnecessary reallocations as we build the graph.
How do I handle weights, like the distance between airports?
The simple vector<int> works for basic connectivity, but usually, you need more data—like the ticket price or the mileage between cities. In this case, you stop storing raw integers and start storing a small struct.
I've found that creating a dedicated Edge struct makes the code much more readable than using a std::pair, which leaves you wondering what first and second actually represent.
struct Edge { int destination; int distance; }; // Now the graph looks like this: std::vector<std::vector<Edge>> weightedAdjLists; void addWeightedFlight(int source, int destination, int dist) { weightedAdjLists[source].push_back({destination, dist}); weightedAdjLists[destination].push_back({source, dist}); }By doing this, you've kept the memory efficiency of the adjacency list while adding the ability to run algorithms like Dijkstra's for finding the shortest path. It's a small change in structure that unlocks a lot of power.
📋 Practical Task
Build a Regional Airport Hub Mapper
Create a program that implements a graph using adjacency lists to model a regional flight network. Your program should:
- Initialize a graph with 6 airports (represented by IDs 0-5).
- Implement a method
addEdge(int u, int v, int weight)that adds a bidirectional flight between two airports with a specific distance. - Implement a method
getConnections(int airportID)that prints all airports reachable from the given ID and the distance to each. - Populate the graph with the following data:
- 0 to 1 (500 miles)
- 0 to 2 (300 miles)
- 1 to 2 (100 miles)
- 2 to 3 (800 miles)
- 3 to 4 (200 miles)
- 4 to 5 (150 miles)
- 5 to 2 (600 miles)
- Call
getConnections(2)and verify that it lists airports 0, 1, 3, and 5 with their correct distances.
There are no comments for now.