techmore.in

C++ Graphs

Graphs are fundamental data structures used to model relationships between entities. In C++, graphs are typically implemented using adjacency lists or adjacency matrices. Here’s an overview of graph concepts, representations, and algorithms.

Graph Basics

  1. Vertices (Nodes): The fundamental units of a graph.
  2. Edges: Connections between vertices. They can be directed (one-way) or undirected (two-way).
  3. Weighted Edges: Edges with associated weights or costs.
  4. Unweighted Edges: Edges without weights.
  5. Directed Graph (Digraph): Edges have a direction.
  6. Undirected Graph: Edges have no direction.
  7. Cyclic Graph: Contains cycles (paths that start and end at the same vertex).
  8. Acyclic Graph: No cycles are present.

Graph Representations

1. Adjacency Matrix

A 2D array where each cell (i, j) indicates whether there is an edge between vertex i and vertex j. For weighted graphs, it stores the weight of the edge.

Advantages:

  • Easy to check if an edge exists.
  • Simple to implement.

Disadvantages:

  • Space complexity is O(V2)O(V^2) (where VV is the number of vertices), which can be inefficient for sparse graphs.

Example:

cpp
#include #include int main() { const int V = 4; // Number of vertices std::vectorint>> adjMatrix(V, std::vector<int>(V, 0)); // Add edges adjMatrix[0][1] = 1; adjMatrix[1][2] = 1; adjMatrix[2][3] = 1; adjMatrix[3][0] = 1; // Print adjacency matrix for (int i = 0; i < V; ++i) { for (int j = 0; j < V; ++j) { std::cout << adjMatrix[i][j] << " "; } std::cout << std::endl; } return 0; }

2. Adjacency List

A list of lists (or arrays) where each list contains the vertices adjacent to a particular vertex. For weighted graphs, each list entry can store a pair of the adjacent vertex and the edge weight.

Advantages:

  • Space complexity is O(V+E)O(V + E) (where EE is the number of edges), which is efficient for sparse graphs.
  • Efficient for iterating over edges.

Disadvantages:

  • Checking for the existence of a particular edge can be slower.

Example:

cpp
#include #include int main() { const int V = 4; // Number of vertices std::vectorint>> adjList(V); // Add edges adjList[0].push_back(1); adjList[1].push_back(2); adjList[2].push_back(3); adjList[3].push_back(0); // Print adjacency list for (int i = 0; i < V; ++i) { std::cout << "Vertex " << i << ":"; for (const int& vertex : adjList[i]) { std::cout << " -> " << vertex; } std::cout << std::endl; } return 0; }

Graph Algorithms

1. Depth-First Search (DFS)

DFS explores as far as possible along each branch before backtracking. It can be implemented using recursion or a stack.

Example:

cpp
#include #include void DFS(int v, const std::vectorint>>& adjList, std::vector<bool>& visited) { visited[v] = true; std::cout << v << " "; for (const int& neighbor : adjList[v]) { if (!visited[neighbor]) { DFS(neighbor, adjList, visited); } } } int main() { const int V = 4; // Number of vertices std::vectorint>> adjList(V); // Add edges adjList[0].push_back(1); adjList[1].push_back(2); adjList[2].push_back(3); adjList[3].push_back(0); std::vector<bool> visited(V, false); // Perform DFS std::cout << "DFS traversal starting from vertex 0: "; DFS(0, adjList, visited); std::cout << std::endl; return 0; }

2. Breadth-First Search (BFS)

BFS explores all neighbors at the present depth level before moving on to nodes at the next depth level. It uses a queue to keep track of the vertices to be explored.

Example:

cpp
#include #include #include void BFS(int start, const std::vectorint>>& adjList) { std::vector<bool> visited(adjList.size(), false); std::queue<int> q; visited[start] = true; q.push(start); while (!q.empty()) { int v = q.front(); q.pop(); std::cout << v << " "; for (const int& neighbor : adjList[v]) { if (!visited[neighbor]) { visited[neighbor] = true; q.push(neighbor); } } } } int main() { const int V = 4; // Number of vertices std::vectorint>> adjList(V); // Add edges adjList[0].push_back(1); adjList[1].push_back(2); adjList[2].push_back(3); adjList[3].push_back(0); // Perform BFS std::cout << "BFS traversal starting from vertex 0: "; BFS(0, adjList); std::cout << std::endl; return 0; }

3. Dijkstra’s Algorithm

Dijkstra’s algorithm finds the shortest paths from a source vertex to all other vertices in a weighted graph with non-negative weights.

Example:

cpp
#include #include #include #include void Dijkstra(int source, const std::vectorint, int>>>& adjList) { int V = adjList.size(); std::vector<int> dist(V, INT_MAX); dist[source] = 0; using pii = std::pair<int, int>; // Pair of (distance, vertex) std::priority_queue, std::greater> pq; pq.push({0, source}); while (!pq.empty()) { int u = pq.top().second; pq.pop(); for (const auto& edge : adjList[u]) { int v = edge.first; int weight = edge.second; if (dist[u] + weight < dist[v]) { dist[v] = dist[u] + weight; pq.push({dist[v], v}); } } } // Print distances for (int i = 0; i < V; ++i) { std::cout << "Distance from " << source << " to " << i << " is " << dist[i] << std::endl; } } int main() { const int V = 5; // Number of vertices std::vectorint, int>>> adjList(V); // Add edges (vertex, weight) adjList[0].emplace_back(1, 10); adjList[0].emplace_back(4, 3); adjList[1].emplace_back(2, 2); adjList[1].emplace_back(4, 4); adjList[2].emplace_back(3, 9); adjList[3].emplace_back(2, 7); adjList[4].emplace_back(1, 1); adjList[4].emplace_back(2, 8); adjList[4].emplace_back(3, 2); // Perform Dijkstra's algorithm std::cout << "Dijkstra's Algorithm starting from vertex 0:" << std::endl; Dijkstra(0, adjList); return 0; }

Key Points

  • Representation: Use adjacency matrices for dense graphs and adjacency lists for sparse graphs.
  • Algorithms: Different algorithms solve various graph-related problems (e.g., shortest paths, connectivity).
  • Complexity: Understand the time and space complexities of different representations and algorithms to choose the most appropriate one for your needs.

Graphs are a versatile data structure used in a wide range of applications, from network routing to social networks. Understanding their representations and algorithms will help you effectively solve problems involving complex relationships between entities.