Understanding Graph Representations
Graphs are fundamental data structures used to model relationships between entities. In network topology simulation, the choice between directed and undirected graphs, and the underlying representation (adjacency list, adjacency matrix, or edge list), directly impacts memory usage, algorithmic efficiency, and code complexity.
Directed vs. Undirected Graphs
A directed graph (digraph) has edges with a direction, meaning the relationship is one-way. For example, a web page link from page A to page B does not imply a link back. An undirected graph treats edges as bidirectional; a connection between two nodes implies mutual adjacency. In network simulations, physical links are often undirected, while routing paths may be directed.
Adjacency List
An adjacency list stores for each vertex a list of its neighbors. For sparse graphs (|E| << |V|²), this is memory-efficient (O(|V|+|E|)) and allows fast iteration over neighbors. In C++, a common implementation is vector<vector<int>> for unweighted graphs or vector<list<pair<int, int>>> for weighted edges. Lookup of a specific edge is O(degree(v)), which is acceptable for most algorithms.
Adjacency Matrix
An adjacency matrix is a 2D array where matrix[u][v] is true (or stores weight) if an edge exists. Edge lookup is O(1), but memory is O(|V|²), making it impractical for large graphs. It is best for dense graphs where edge existence checks are frequent.
Edge List with Vertex Array
This representation stores vertices in an array and edges as pairs of vertex indices. It is compact and works well for undirected graphs (each edge stored once). However, removing a vertex requires scanning all edges to update references, which can be O(|E|). To mitigate, maintain adjacency lists for quick incident edge lookup.
Performance Considerations
- Use index-based references (integers) instead of pointers to avoid memory fragmentation and improve cache locality.
- For dynamic graphs with frequent deletions, consider lazy deletion: mark nodes as removed and skip them during traversal, then periodically compact the arrays.
- For large-scale simulations, leverage optimized libraries like Boost.Graph or LEMON, which provide efficient graph containers and algorithms.
Conclusion
The best representation depends on your operations: if neighbor iteration is critical, adjacency lists win; if edge existence checks dominate, a matrix may be better. For mesh network simulations with dynamic changes, a hybrid approach often works best.


