Skip to content

A modern C++ library for graph data structures and algorithms with template-based flexibility and type safety.

Notifications You must be signed in to change notification settings

yanjiulab/graphx

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GraphX

A modern C++ library for graph data structures and algorithms with template-based flexibility and type safety.

Features

  • Multiple Graph Types: Support for both undirected (Graph) and directed graphs (DiGraph)
  • Generic Data Types: Flexible node and edge data storage with template parameters
  • Graph Operations: Add/remove nodes and edges with efficient adjacency list implementation
  • Comprehensive Algorithms:
    • Traversal: BFS, DFS
    • Shortest Paths: 8 algorithms (Dijkstra, Bellman-Ford, A*, Floyd-Warshall, Johnson, Bidirectional Dijkstra, Yen K-Shortest Paths, Suurballe)
    • Components: Connected Components, Strongly Connected Components
    • Ordering: Topological Sort
    • Maximum Flow: Dinic's Algorithm, Edmonds-Karp Algorithm
    • Graph Construction: Factory Pattern, Fluent Builder Interface
  • Graph I/O: Edge List, GraphML, DIMACS formats
  • Type Safety: Compile-time type checking for node IDs, node data, and edge data
  • C++17 Compatible: Modern C++ features with full type safety
  • Header-only: No external dependencies

Quick Start

Dependency

No dependencies! This project is header-only.

Building

cd graphx
mkdir -p build && cd build
cmake ..
make

Running Demos

# Basic API Operations
./demo_api_basics

# Graph Traversal Algorithms (BFS, DFS, Components, SCC)
./demo_traversals

# 8 Shortest Path Algorithms
./demo_shortest_paths

# Graph I/O Operations (Edge List, GraphML, DIMACS)
./demo_io

# Factory Pattern and Builder Interface
./demo_factory

# Maximum Flow Algorithms (Edmonds-Karp, Dinic)
./demo_maxflow

See DEMO.md for detailed demo information.

Creating an Undirected Graph

#include "GraphX/Graph.hpp"
using namespace GraphX;

Graph<int, std::string, double> g;

// Add nodes
g.add_node(1, "Beijing");
g.add_node(2, "Shanghai");

// Add edges
g.add_edge(1, 2, 1064.7);  // Distance in km

// Query graph properties
std::cout << "Nodes: " << g.node_count() << "\n";
std::cout << "Node 1: " << g[1] << "\n";

// Get neighbors
for (auto neighbor : g.neighbors(1)) {
    std::cout << "Neighbor: " << neighbor << "\n";
}

Using Algorithms

#include "GraphX/GraphAlgorithms.hpp"

// BFS traversal
algorithms::bfs(g, 1, [](int node) {
    std::cout << "Visiting: " << node << "\n";
});

// Dijkstra's shortest path
auto distances = algorithms::shortest_paths::Dijkstra<decltype(g)>::compute(g, 1);

// Connected components
auto components = algorithms::connected_components(g);

// Topological sort for DAG
auto topo_order = algorithms::topological_sort(dag);

Graph Construction with Factory & Builder

#include "GraphX/GraphFactory.hpp"

// Using Factory
auto graph = GraphFactory::create_graph<int, std::string, double>();

// Using Builder (Fluent Interface)
auto network = GraphBuilder<Graph<int, std::string, double>>()
    .add_node(1, "A")
    .add_node(2, "B")
    .add_node(3, "C")
    .add_edge(1, 2, 10.0)
    .add_edge(2, 3, 20.0)
    .build();

// Creating from edge list
std::vector<std::tuple<int, int, double>> edges = {
    {1, 2, 10.0},
    {2, 3, 20.0}
};
auto g = GraphFactory::from_edge_list(edges);

Maximum Flow

#include "GraphX/algorithms/maxflow_dinic.hpp"
#include "GraphX/algorithms/maxflow_edmonds_karp.hpp"

// Using Dinic's Algorithm (faster)
auto result = algorithms::maxflow::Dinic<DiGraph>::compute(g, source, sink);
std::cout << "Max Flow: " << result.max_flow << "\n";

// Using Edmonds-Karp Algorithm
auto ek_result = algorithms::maxflow::EdmondsKarp<DiGraph>::compute(g, source, sink);

Graph I/O

#include "GraphX/GraphIO.hpp"

// Write edge list
io::EdgeListIO<Graph>::write("graph.txt", g);

// Write GraphML
io::GraphMLIO<Graph>::write("graph.graphml", g);

// Write DIMACS
io::DIMACS_IO<Graph>::write("graph.dimacs", g, 1, 4);

// Read edge list
auto g2 = io::EdgeListIO<Graph>::read("graph.txt");

Project Structure

graphx/
├── include/GraphX/
│   ├── Graph.hpp
│   ├── DiGraph.hpp
│   ├── GraphFactory.hpp
│   ├── GraphAlgorithms.hpp
│   ├── GraphIO.hpp
│   ├── algorithms/
│   │   ├── bfs.hpp
│   │   ├── dfs.hpp
│   │   ├── connected_components.hpp
│   │   ├── strongly_connected_components.hpp
│   │   ├── topological_sort.hpp
│   │   ├── detail/
│   │   │   └── shortest_path_common.hpp
│   │   ├── shortest_paths/
│   │   │   ├── dijkstra.hpp
│   │   │   ├── bellman_ford.hpp
│   │   │   ├── astar.hpp
│   │   │   ├── bidirectional_dijkstra.hpp
│   │   │   ├── floyd_warshall.hpp
│   │   │   ├── johnson.hpp
│   │   │   ├── yen_ksp.hpp
│   │   │   └── suurballe.hpp
│   │   ├── shortest_paths.hpp
│   │   ├── maxflow_dinic.hpp
│   │   └── maxflow_edmonds_karp.hpp
│   └── io/
│       ├── edge_list.hpp
│       ├── graphml.hpp
│       └── dimacs.hpp
├── src/
│   ├── demo_api_basics.cpp
│   ├── demo_traversals.cpp
│   ├── demo_shortest_paths.cpp
│   ├── demo_io.cpp
│   ├── demo_factory.cpp
│   ├── demo_maxflow.cpp
│   └── demo_extended.cpp
├── doc/
│   ├── algorithms/
│   │   ├── bfs.md
│   │   ├── bfs_zh.md
│   │   ├── dfs.md
│   │   ├── dfs_zh.md
│   │   ├── connected_components.md
│   │   ├── connected_components_zh.md
│   │   ├── strongly_connected_components.md
│   │   ├── strongly_connected_components_zh.md
│   │   ├── topological_sort.md
│   │   ├── topological_sort_zh.md
│   │   ├── shortest_paths.md
│   │   ├── shortest_paths_zh.md
│   │   ├── dijkstra.md
│   │   ├── dijkstra_zh.md
│   │   ├── bellman_ford.md
│   │   ├── bellman_ford_zh.md
│   │   ├── astar.md
│   │   ├── astar_zh.md
│   │   ├── bidirectional_dijkstra.md
│   │   ├── bidirectional_dijkstra_zh.md
│   │   ├── floyd_warshall.md
│   │   ├── floyd_warshall_zh.md
│   │   ├── johnson.md
│   │   ├── johnson_zh.md
│   │   ├── yen_ksp.md
│   │   ├── yen_ksp_zh.md
│   │   ├── suurballe.md
│   │   ├── suurballe_zh.md
│   │   ├── maxflow_edmonds_karp.md
│   │   ├── maxflow_edmonds_karp_zh.md
│   │   ├── maxflow_dinic.md
│   │   ├── maxflow_dinic_zh.md
│   │   ├── detail_shortest_path_common.md
│   │   └── detail_shortest_path_common_zh.md
│   └── README.md
├── CMakeLists.txt
├── README.md
├── DEMO_README.md
└── C++14_COMPATIBILITY.md

Algorithm Reference

Traversal Algorithms

Algorithm File Time Space Use Case
BFS bfs.hpp O(V+E) O(V) Shortest path in unweighted graphs, layer-by-layer traversal
DFS dfs.hpp O(V+E) O(V) Topological sort, cycle detection, SCC preparation

Component Algorithms

Algorithm File Time Space Use Case
Connected Components connected_components.hpp O(V+E) O(V) Find all components in undirected graph
Strongly Connected Components strongly_connected_components.hpp O(V+E) O(V) Find SCCs in directed graph using Tarjan
Topological Sort topological_sort.hpp O(V+E) O(V) DAG ordering using Kahn or DFS

Shortest Path Algorithms

Algorithm File Time Space Best For
Dijkstra shortest_paths/dijkstra.hpp O((V+E)logV) O(V) Non-negative weights, single-source
Bellman-Ford shortest_paths/bellman_ford.hpp O(VE) O(V) Negative weights, negative cycle detection
A* shortest_paths/astar.hpp Variable O(V) Pathfinding with heuristic
Bidirectional Dijkstra shortest_paths/bidirectional_dijkstra.hpp O((V+E)logV) O(V) Point-to-point queries, often faster
Floyd-Warshall shortest_paths/floyd_warshall.hpp O(V³) O(V²) All-pairs, small/dense graphs
Johnson shortest_paths/johnson.hpp O(VE + V²logV) O(V²) All-pairs, sparse graphs
Yen's K-Shortest Paths shortest_paths/yen_ksp.hpp O(K·V·E·logV) O(V+E) Find K loopless shortest paths
Suurballe shortest_paths/suurballe.hpp O((V+E)logV) O(V+E) Two edge-disjoint shortest paths

Maximum Flow Algorithms

Algorithm File Time Space Best For
Edmonds-Karp maxflow_edmonds_karp.hpp O(VE²) O(V+E) Simple, reliable, educational
Dinic maxflow_dinic.hpp O(V²E) O(V+E) Large networks, practical efficiency

API Examples

Basic Operations

Graph<int, std::string, double> g;

// Add nodes
g.add_node(1, "Node A");
g.add_node(2, "Node B");

// Query nodes
g.has_node(1);          // bool
g.node_count();         // size_t
g.nodes();              // vector<NodeId>

// Access/modify node data
auto data = g[1];
g[1] = "Updated Label";

// Add/remove edges
g.add_edge(1, 2, 5.0);
g.remove_edge(1, 2);

// Query edges
g.has_edge(1, 2);
g.edge(1, 2);           // Get edge weight
g.degree(1);            // Node degree
g.neighbors(1);         // Adjacent nodes
g.edges();              // All edges as vector<tuple<u,v,w>>

Directed Graph Operations

DiGraph<int, std::string, double> dg;

dg.add_node(1, "A");
dg.add_edge(1, 2, 1.0);

// Directed-specific queries
dg.in_degree(2);        // Number of incoming edges
dg.out_degree(1);       // Number of outgoing edges
dg.predecessors(2);     // Nodes pointing to 2
dg.successors(1);       // Nodes pointed to by 1
dg.is_directed();       // bool (true for DiGraph)

Factory and Builder Pattern

// Factory method
auto g1 = GraphFactory::create_graph<int, std::string, double>();

// Builder with fluent interface
auto g2 = GraphBuilder<Graph<int, std::string, double>>()
    .add_node(1, "A")
    .add_node(2, "B")
    .add_edge(1, 2, 10.0)
    .build();

// From edge list
std::vector<std::tuple<int, int, double>> edges = {{1,2,10}, {2,3,20}};
auto g3 = GraphFactory::from_edge_list(edges);

// From directed edge list
auto g4 = GraphFactory::from_directed_edge_list(edges);

Documentation

Comprehensive algorithm documentation is available in the doc/ directory with both English and Chinese versions:

  • Traversal: BFS, DFS
  • Components: Connected Components, Strongly Connected Components
  • Shortest Paths: All 8 algorithms with complexity analysis and use cases
  • Maximum Flow: Edmonds-Karp and Dinic with real-world examples
  • Utilities: Common helpers and implementation notes

Each algorithm document includes:

  • Algorithm overview and key concepts
  • Time and space complexity analysis
  • Public API documentation
  • Implementation notes specific to this project
  • Example usage patterns
  • References

Access with doc/algorithm_name.md (English) or doc/algorithm_name_zh.md (Chinese).

Building Requirements

  • C++17 or later (main branch)
  • C++14 compatible version available with configuration
  • CMake 3.10+
  • Standard C++ library (no external dependencies)

Demo Programs

The project includes six comprehensive demo programs showcasing different features:

  1. demo_api_basics - Graph construction, node/edge operations, queries
  2. demo_traversals - BFS, DFS, connected components, strongly connected components
  3. demo_shortest_paths - All 8 shortest path algorithms with comparisons
  4. demo_io - Reading/writing graphs in multiple formats
  5. demo_factory - Factory patterns and builder interface for graph construction
  6. demo_maxflow - Maximum flow algorithms with real-world applications

Run all demos:

cd build
./demo_api_basics && ./demo_traversals && ./demo_shortest_paths && \
./demo_io && ./demo_factory && ./demo_maxflow

Type System

GraphX uses a flexible template-based type system:

// NodeId: type of node identifiers (int, string, etc.)
// NodeData: data stored at each node (string, struct, etc.)
// EdgeData: data stored at each edge/weight (double, struct, etc.)

Graph<NodeId, NodeData, EdgeData> g;

// Example: City network with populations and distances
Graph<int, std::string, double> cities;

// Example: Program dependency graph with names and artifact sizes
Graph<std::string, std::pair<std::string, int>, double> build_graph;

Performance Characteristics

Operation Time Notes
Add node O(1) Hash table insertion
Remove node O(V + E) Must remove from all adjacency lists
Add edge O(1) Hash table insertion
Remove edge O(degree) Linear search in edge vector
Query has_edge O(degree) Linear search in edge vector
Get neighbors O(degree) Returns vector of neighbors
Get node count O(1) Cached
Get edge count O(1) Cached
Get degree O(1) Cached in node info
Get all nodes O(V) Returns copy of node list
Get all edges O(V + E) Iterates all adjacency lists

Contributing

Contributions are welcome! Please:

  • Follow the existing code style
  • Add tests for new features
  • Update documentation (English and Chinese)
  • Include example code in demos
  • Submit pull requests with clear descriptions

License

MIT License

References

  • Cormen, Leiserson, Rivest, Stein - Introduction to Algorithms (CLRS)
  • Tarjan - Depth-First Search and Linear Graph Algorithms (1972)
  • Dijkstra - A Note on Two Problems in Connexion with Graphs (1959)
  • Bellman - On a Routing Problem (1958)
  • Floyd - Algorithm 97: Shortest Path (1962)
  • Yen - Finding the K Shortest Loopless Paths in a Network (1971)
  • Suurballe - Disjoint Paths in a Network (1974)
  • Edmonds & Karp - Theoretical Improvements in Algorithmic Efficiency (1972)
  • Dinic - Algorithm for Solution of a Problem of Maximum Flow in Networks (1970)

Acknowledgments

This library is designed for educational and practical applications, combining modern C++ practices with classical graph algorithms.

About

A modern C++ library for graph data structures and algorithms with template-based flexibility and type safety.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published