C++ class templates for graph construction and search
Learning Objectives: Create your first graph, add vertices and edges, and understand core libgraph concepts.
Estimated Time: 15 minutes
In this tutorial, you’ll learn the fundamental operations for building and manipulating graphs with libgraph. We’ll create a simple transportation network and explore the basic API.
Let’s build a simple city transportation network:
#include "graph/graph.hpp"
#include <iostream>
#include <vector>
using namespace xmotion;
// Define our state type - represents locations in the city
struct Location {
int id;
std::string name;
// Constructor for convenience
Location(int i, const std::string& n) : id(i), name(n) {}
};
int main() {
// Step 1: Create the graph
Graph<Location> city_map;
// Step 2: Add vertices (locations in our city)
Location home{0, "Home"};
Location work{1, "Work"};
Location gym{2, "Gym"};
Location store{3, "Store"};
Location park{4, "Park"};
city_map.AddVertex(home);
city_map.AddVertex(work);
city_map.AddVertex(gym);
city_map.AddVertex(store);
city_map.AddVertex(park);
// Step 3: Connect locations with weighted edges (distance in km)
city_map.AddEdge(home, work, 5.2); // Home to Work: 5.2km
city_map.AddEdge(home, gym, 2.8); // Home to Gym: 2.8km
city_map.AddEdge(home, store, 1.5); // Home to Store: 1.5km
city_map.AddEdge(work, gym, 3.1); // Work to Gym: 3.1km
city_map.AddEdge(gym, park, 1.2); // Gym to Park: 1.2km
city_map.AddEdge(store, park, 2.0); // Store to Park: 2.0km
// Step 4: Explore the graph we created
std::cout << "=== City Transportation Network ===" << std::endl;
std::cout << "Total locations: " << city_map.GetVertexCount() << std::endl;
std::cout << "Total connections: " << city_map.GetEdgeCount() << std::endl;
// Step 5: Check connectivity and distances
std::cout << "\n=== Connectivity Check ===" << std::endl;
std::cout << "Can go from Home to Work? " << (city_map.HasEdge(home, work) ? "Yes" : "No") << std::endl;
std::cout << "Distance from Home to Work: " << city_map.GetEdgeWeight(home, work) << " km" << std::endl;
std::cout << "Can go from Work to Home? " << (city_map.HasEdge(work, home) ? "Yes" : "No") << std::endl;
// Step 6: Explore neighbors
auto home_neighbors = city_map.GetNeighbors(home);
std::cout << "\nPlaces reachable from Home:" << std::endl;
for (const auto& neighbor : home_neighbors) {
std::cout << " -> " << neighbor.name << " (ID: " << neighbor.id << ")" << std::endl;
}
// Step 7: Use iterators to examine all vertices
std::cout << "\n=== All Locations ===" << std::endl;
for (auto it = city_map.vertex_begin(); it != city_map.vertex_end(); ++it) {
const auto& location = it->state;
size_t out_degree = city_map.GetOutDegree(location.id);
std::cout << location.name << " (ID: " << location.id
<< ", outgoing connections: " << out_degree << ")" << std::endl;
}
// Step 8: Range-based for loop (modern C++)
std::cout << "\n=== Using Range-Based For Loop ===" << std::endl;
for (const auto& vertex : city_map.vertices()) {
std::cout << "Location: " << vertex.state.name << std::endl;
}
return 0;
}
struct Location {
int id;
std::string name;
Location(int i, const std::string& n) : id(i), name(n) {}
};
Key Points:
id
field is automatically detected by DefaultIndexer
Graph<Location> city_map;
Template Parameters:
Location
: Our vertex state typedouble
: Edge weight type (default)DefaultIndexer<Location>
: State indexing (default, uses id
field)city_map.AddVertex(home);
Important Notes:
city_map.AddEdge(home, work, 5.2);
Edge Behavior:
home
to work
with weight 5.2
bool connected = city_map.HasEdge(home, work);
double distance = city_map.GetEdgeWeight(home, work);
auto neighbors = city_map.GetNeighbors(home);
Query Methods:
HasEdge()
: Check if direct connection existsGetEdgeWeight()
: Get edge weight (returns default value if no edge)GetNeighbors()
: Get all directly reachable statessize_t vertex_count = city_map.GetVertexCount();
size_t edge_count = city_map.GetEdgeCount();
size_t out_degree = city_map.GetOutDegree(location.id);
Statistics Available:
city_map.empty()
// Traditional iterators
for (auto it = city_map.vertex_begin(); it != city_map.vertex_end(); ++it) {
const Location& loc = it->state;
}
// Range-based for loop (recommended)
for (const auto& vertex : city_map.vertices()) {
const Location& loc = vertex.state;
}
DefaultIndexer
automatically uses id
, id_
, or GetId()
methodAddEdge(A, B, weight)
creates A → B (directed)AddUndirectedEdge(A, B, weight)
creates A ↔ B (bidirectional)State
can be any copyable typeTransition
(edge weight) can be numeric or custom typeSave the code as basic_graph_tutorial.cpp
and compile:
# Assuming libgraph is in your include path
g++ -std=c++11 -I/path/to/libgraph/include basic_graph_tutorial.cpp -o basic_graph_tutorial
# Run the program
./basic_graph_tutorial
Expected Output:
=== City Transportation Network ===
Total locations: 5
Total connections: 6
=== Connectivity Check ===
Can go from Home to Work? Yes
Distance from Home to Work: 5.2 km
Can go from Work to Home? No
Places reachable from Home:
-> Work (ID: 1)
-> Gym (ID: 2)
-> Store (ID: 3)
=== All Locations ===
Home (ID: 0, outgoing connections: 3)
Work (ID: 1, outgoing connections: 1)
Gym (ID: 2, outgoing connections: 1)
Store (ID: 3, outgoing connections: 1)
Park (ID: 4, outgoing connections: 0)
=== Using Range-Based For Loop ===
Location: Home
Location: Work
Location: Gym
Location: Store
Location: Park
Modify the code to make some connections bidirectional (like between Home and Store for a round trip).
Create a graph using a different state type, like struct Person { int id; std::string name; int age; };