Logo

dev-resources.site

for different kinds of informations.

Graphs, Data Structures

Published at
6/5/2024
Categories
dsa
datastructures
graphs
Author
harshm03
Categories
3 categories in total
dsa
open
datastructures
open
graphs
open
Author
8 person written this
harshm03
open
Graphs, Data Structures

Graphs

Graphs are fundamental data structures in computer science and discrete mathematics. They are used to represent pairwise relations between objects. Understanding the theoretical underpinnings of graphs is essential for leveraging their full potential in various applications.

What is a Graph?

A graph G consists of a set of vertices V and a set of edges E connecting pairs of vertices. Formally, a graph is defined as G = (V, E).

Types of Graphs

  • Undirected Graphs: In an undirected graph, edges have no direction. If there is an edge between vertices u and v, it can be traversed in both directions.
  • Directed Graphs (Digraphs): In a directed graph, edges have a direction. An edge from vertex u to vertex v is denoted as (u, v), indicating the direction from u to v.

Graph Terminology

  • Vertex (Node): A fundamental unit of a graph.
  • Edge (Link): A connection between two vertices.
  • Degree: The number of edges incident to a vertex.
  • Path: A sequence of vertices where each adjacent pair is connected by an edge.

Graph Representations

  • Adjacency Matrix: A 2D array where matrix[i][j] = 1 if there is an edge from vertex i to vertex j, otherwise 0.
    • Pros: Simple, quick edge existence check.
    • Cons: Memory-intensive for sparse graphs.
  • Adjacency List: An array of lists. Each index i contains a list of vertices that are adjacent to vertex i.
    • Pros: Space-efficient for sparse graphs, faster traversal.
    • Cons: Slower edge existence check compared to adjacency matrix.

Graph Implementation Using Adjacency Matrix in C++

Graph implementation using an adjacency matrix in C++ provides a compact and straightforward way to represent graphs. Adjacency matrices are suitable for dense graphs where most vertices are connected, as they offer constant-time edge existence checks.

Creation of the Graph

To create a graph using an adjacency matrix in C++, we define a class that contains the basic attributes and methods required to manage the graph. Below is the initial part of the class definition, including the basic attributes and the constructor.

#include <iostream>
#include <vector>
using namespace std;

class Graph {
private:
    int V;  // Number of vertices
    vector<vector<int>> adj;  // Adjacency matrix

public:
    Graph(int vertices) {
        V = vertices;
        adj.resize(V, vector<int>(V, 0)); // Initialize adjacency matrix with all zeros
    }

    // Other methods for graph operations will be added here
};
Enter fullscreen mode Exit fullscreen mode

Attributes Explanation

  1. V: This integer variable stores the number of vertices in the graph.

  2. adj: This 2D vector serves as the adjacency matrix to represent the connections between vertices. Each element adj[i][j] indicates whether there is an edge from vertex i to vertex j. Initialized with all zeros in the constructor.

Constructor Explanation

The constructor Graph(int vertices) initializes the graph with a specified number of vertices:

  • V = vertices: Sets the number of vertices in the graph to the specified value.
  • adj.resize(V, vector(V, 0)): Resizes the adjacency matrix to V x V size and initializes all elements to 0, indicating no edges initially.

This setup provides the basic framework for the graph, allowing us to build upon it with additional methods for operations such as adding edges, traversing the graph, and finding shortest paths. The constructor ensures that the graph is properly initialized with the specified number of vertices and is ready for further operations.

Operations on Graph

Operations on graphs involve various tasks such as adding or removing edges, traversing the graph, finding shortest paths, and detecting cycles. Each operation serves a specific purpose in graph manipulation and analysis.

Fundamental operations on graphs include adding and removing edges, which modify the connectivity between vertices.

Adding Edge

Adding an edge to a graph establishes a connection between two vertices, creating a relationship between them.

void addEdge(int u, int v) {
    adj[u][v] = 1;
    // For undirected graphs, uncomment the line below
    // adj[v][u] = 1;
}
Enter fullscreen mode Exit fullscreen mode

Time Complexity: O(1)

Removing Edge

Removing an edge from a graph disconnects two vertices, removing the relationship between them.

void removeEdge(int u, int v) {
    adj[u][v] = 0;
    // For undirected graphs, uncomment the line below
    // adj[v][u] = 0;
}
Enter fullscreen mode Exit fullscreen mode

Time Complexity: O(1)

These fundamental operations allow us to modify the structure of the graph by adding or removing connections between vertices. They are essential building blocks for more complex graph algorithms and operations.

Graph Traversal

Graph traversal involves visiting all the vertices of a graph in a systematic way. Traversal algorithms help explore the structure of the graph and can be used to perform various tasks such as finding paths, detecting cycles, and discovering connected components.

Depth-First Search (DFS)

Depth-First Search (DFS) is a graph traversal algorithm that explores as far as possible along each branch before backtracking. It starts at a chosen vertex and explores as far as possible along each branch before backtracking.

#include <stack>

void DFS(int start) {
    vector<bool> visited(V, false);
    stack<int> stk;

    stk.push(start);
    visited[start] = true;

    while (!stk.empty()) {
        int v = stk.top();
        stk.pop();
        cout << v << " ";

        for (int i = 0; i < V; ++i) {
            if (adj[v][i] && !visited[i]) {
                visited[i] = true;
                stk.push(i);
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Time Complexity: O(V + E)

Breadth-First Search (BFS)

Breadth-First Search (BFS) is a graph traversal algorithm that explores all the neighboring vertices of a chosen vertex before moving to the next level of vertices.

#include <queue>

void BFS(int start) {
    vector<bool> visited(V, false);
    queue<int> q;

    q.push(start);
    visited[start] = true;

    while (!q.empty()) {
        int v = q.front();
        q.pop();
        cout << v << " ";

        for (int i = 0; i < V; ++i) {
            if (adj[v][i] && !visited[i]) {
                visited[i] = true;
                q.push(i);
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Time Complexity: O(V + E)

Graph traversal algorithms provide essential mechanisms for exploring the structure of graphs and discovering their properties. They are foundational in graph analysis and play a crucial role in various graph-based algorithms and applications.

Full Code Implementation of Graphs Using Adjacency Matrix in C++

Graph implementation using an adjacency matrix in C++ provides a compact and straightforward way to represent graphs. Adjacency matrices are suitable for dense graphs where most vertices are connected, as they offer constant-time edge existence checks.

This implementation includes the creation of a graph class with methods for adding and removing edges, as well as traversal algorithms such as depth-first search (DFS) and breadth-first search (BFS).

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
using namespace std;

class Graph {
private:
    int V;  // Number of vertices
    vector<vector<int>> adj;  // Adjacency matrix

public:
    Graph(int vertices) {
        V = vertices;
        adj.resize(V, vector<int>(V, 0)); // Initialize adjacency matrix with all zeros
    }

    void addEdge(int u, int v) {
        adj[u][v] = 1;
        // For undirected graphs, uncomment the line below
        // adj[v][u] = 1;
    }

    void removeEdge(int u, int v) {
        adj[u][v] = 0;
        // For undirected graphs, uncomment the line below
        // adj[v][u] = 0;
    }

    void DFS(int start) {
        vector<bool> visited(V, false);
        stack<int> stk;

        stk.push(start);
        visited[start] = true;

        while (!stk.empty()) {
            int v = stk.top();
            stk.pop();
            cout << v << " ";

            for (int i = 0; i < V; ++i) {
                if (adj[v][i] && !visited[i]) {
                    visited[i] = true;
                    stk.push(i);
                }
            }
        }
    }

    void BFS(int start) {
        vector<bool> visited(V, false);
        queue<int> q;

        q.push(start);
        visited[start] = true;

        while (!q.empty()) {
            int v = q.front();
            q.pop();
            cout << v << " ";

            for (int i = 0; i < V; ++i) {
                if (adj[v][i] && !visited[i]) {
                    visited[i] = true;
                    q.push(i);
                }
            }
        }
    }
};

int main() {
    // Create a graph with 5 vertices
    Graph graph(5);

    // Add some edges
    graph.addEdge(0, 1);
    graph.addEdge(0, 2);
    graph.addEdge(1, 3);
    graph.addEdge(2, 4);

    cout << "Depth-First Search (DFS): ";
    graph.DFS(0);
    cout << endl;

    cout << "Breadth-First Search (BFS): ";
    graph.BFS(0);
    cout << endl;

    return 0;
}
Enter fullscreen mode Exit fullscreen mode

This code demonstrates the creation of a graph class using an adjacency matrix representation in C++. It includes methods for adding and removing edges, as well as depth-first search (DFS) and breadth-first search (BFS) traversal algorithms.

Graph Implementation Using Adjacency List in C++

Graph implementation using an adjacency list in C++ provides a flexible and memory-efficient way to represent graphs. Adjacency lists are suitable for sparse graphs where only a few vertices are connected, as they offer efficient memory usage and traversal.

Creation of the Graph

To create a graph using an adjacency list in C++, we define a class that contains the basic attributes and methods required to manage the graph. Below is the initial part of the class definition, including the basic attributes and the constructor.

#include <iostream>
#include <vector>
using namespace std;

class Graph {
private:
    int V;  // Number of vertices
    vector<vector<int>> adj;  // Adjacency list

public:
    Graph(int vertices) {
        V = vertices;
        adj.resize(V); // Initialize adjacency list
    }

    // Other methods for graph operations will be added here
};
Enter fullscreen mode Exit fullscreen mode

Attributes Explanation

  1. V: This integer variable stores the number of vertices in the graph.

  2. adj: This vector of vectors serves as the adjacency list to represent the connections between vertices. Each vector adj[i] contains the indices of vertices adjacent to vertex i.

Constructor Explanation

The constructor Graph(int vertices) initializes the graph with a specified number of vertices:

  • V = vertices: Sets the number of vertices in the graph to the specified value.
  • adj.resize(V): Resizes the adjacency list to V size, initializing it with empty vectors for each vertex.

This setup provides the basic framework for the graph, allowing us to build upon it with additional methods for operations such as adding edges, traversing the graph, and finding shortest paths. The constructor ensures that the graph is properly initialized with the specified number of vertices and is ready for further operations.

Operations on Graph

Operations on graphs involve various tasks such as adding or removing edges, traversing the graph, finding shortest paths, and detecting cycles. Each operation serves a specific purpose in graph manipulation and analysis.

Fundamental operations on graphs include adding and removing edges, which modify the connectivity between vertices.

Adding Edge

Adding an edge to a graph establishes a connection between two vertices, creating a relationship between them.

void addEdge(int u, int v) {
    adj[u].push_back(v);
    // For undirected graphs, uncomment the line below
    // adj[v].push_back(u);
}
Enter fullscreen mode Exit fullscreen mode

Time Complexity: O(1)

Removing Edge

Removing an edge from a graph disconnects two vertices, removing the relationship between them.

void removeEdge(int u, int v) {
    for (int i = 0; i < adj[u].size(); ++i) {
        if (adj[u][i] == v) {
            adj[u].erase(adj[u].begin() + i);
            break;
        }
    }
    // For undirected graphs, uncomment the lines below
    // for (int i = 0; i < adj[v].size(); ++i) {
    //     if (adj[v][i] == u) {
    //         adj[v].erase(adj[v].begin() + i);
    //         break;
    //     }
    // }
}
Enter fullscreen mode Exit fullscreen mode

Time Complexity: O(V), where V is the number of vertices in the graph.

These fundamental operations allow us to modify the structure of the graph by adding or removing connections between vertices. They are essential building blocks for more complex graph algorithms and operations.

Graph Traversal

Graph traversal involves visiting all the vertices of a graph in a systematic way. Traversal algorithms help explore the structure of the graph and can be used to perform various tasks such as finding paths, detecting cycles, and discovering connected components.

Depth-First Search (DFS)

Depth-First Search (DFS) is a graph traversal algorithm that explores as far as possible along each branch before backtracking. It starts at a chosen vertex and explores as far as possible along each branch before backtracking.

#include <stack>

void DFS(int start) {
    vector<bool> visited(V, false);
    stack<int> stk;

    stk.push(start);
    visited[start] = true;

    while (!stk.empty()) {
        int v = stk.top();
        stk.pop();
        cout << v << " ";

        for (int i = 0; i < adj[v].size(); ++i) {
            int u = adj[v][i];
            if (!visited[u]) {
                visited[u] = true;
                stk.push(u);
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Time Complexity: O(V + E)

Breadth-First Search (BFS)

Breadth-First Search (BFS) is a graph traversal algorithm that explores all the neighboring vertices of a chosen vertex before moving to the next level of vertices.

#include <queue>

void BFS(int start) {
    vector<bool> visited(V, false);
    queue<int> q;

    q.push(start);
    visited[start] = true;

    while (!q.empty()) {
        int v = q.front();
        q.pop();
        cout << v << " ";

        for (int i = 0; i < adj[v].size(); ++i) {
            int u = adj[v][i];
            if (!visited[u]) {
                visited[u] = true;
                q.push(u);
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Time Complexity: O(V + E)

Graph traversal algorithms provide essential mechanisms for exploring the structure of graphs and discovering their properties. They are foundational in graph analysis and play a crucial role in various graph-based algorithms and applications.

Full Code Implementation of Graphs Using Adjacency List in C++

Graph implementation using an adjacency list in C++ provides a flexible and memory-efficient way to represent graphs. Adjacency lists are suitable for sparse graphs where few vertices are connected, as they offer efficient memory usage and traversal operations.

This implementation includes the creation of a graph class with methods for adding and removing edges, as well as traversal algorithms such as depth-first search (DFS) and breadth-first search (BFS).

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
using namespace std;

class Graph {
private:
    int V;  // Number of vertices
    vector<vector<int>> adj;  // Adjacency list

public:
    Graph(int vertices) {
        V = vertices;
        adj.resize(V); // Initialize adjacency list
    }

    void addEdge(int u, int v) {
        adj[u].push_back(v);
        // For undirected graphs, uncomment the line below
        // adj[v].push_back(u);
    }

    void removeEdge(int u, int v) {
        for (auto it = adj[u].begin(); it != adj[u].end(); ++it) {
            if (*it == v) {
                adj[u].erase(it);
                break;
            }
        }
        // For undirected graphs, uncomment the line below
        // for (auto it = adj[v].begin(); it != adj[v].end(); ++it) {
        //     if (*it == u) {
        //         adj[v].erase(it);
        //         break;
        //     }
        // }
    }

    void DFS(int start) {
        vector<bool> visited(V, false);
        stack<int> stk;

        stk.push(start);
        visited[start] = true;

        while (!stk.empty()) {
            int v = stk.top();
            stk.pop();
            cout << v << " ";

            for (int u : adj[v]) {
                if (!visited[u]) {
                    visited[u] = true;
                    stk.push(u);
                }
            }
        }
    }

    void BFS(int start) {
        vector<bool> visited(V, false);
        queue<int> q;

        q.push(start);
        visited[start] = true;

        while (!q.empty()) {
            int v = q.front();
            q.pop();
            cout << v << " ";

            for (int u : adj[v]) {
                if (!visited[u]) {
                    visited[u] = true;
                    q.push(u);
                }
            }
        }
    }
};

int main() {
    // Create a graph with 5 vertices
    Graph graph(5);

    // Add some edges
    graph.addEdge(0, 1);
    graph.addEdge(0, 2);
    graph.addEdge(1, 3);
    graph.addEdge(2, 4);

    cout << "Depth-First Search (DFS): ";
    graph.DFS(0);
    cout << endl;

    cout << "Breadth-First Search (BFS): ";
    graph.BFS(0);
    cout << endl;

    return 0;
}
Enter fullscreen mode Exit fullscreen mode

This code demonstrates the creation of a graph class using an adjacency list representation in C++. It includes methods for adding and removing edges, as well as depth-first search (DFS) and breadth-first search (BFS) traversal algorithms.

graphs Article's
30 articles in total
Favicon
Converting Plotly charts into images in parallel
Favicon
TeeChart Charting Libraries use cases
Favicon
Graphs, Data Structures
Favicon
Navigating the Evolution of AI in Cybersecurity: Insights from Mastercard at #RiskX 2023
Favicon
Graph Coloring Problem: Cracking Complexity with Elegant Solutions
Favicon
Personal Knowledge Graphs in Relational Model
Favicon
Closing a chapter
Favicon
Grouping algorithm
Favicon
Priority Queue vs Set
Favicon
Algorithmic Alchemy: Exploiting Graph Theory in the Foreign Exchange
Favicon
Apache AGE: Unique Use Cases
Favicon
Efficient Representation of Relationships with Graph Databases
Favicon
Crafting Mazes with Graph Theory
Favicon
Embracing the Power of Graph Databases
Favicon
Exploring Graph Visualisation with Apache AGE: Unveiling Hidden Insights
Favicon
Introductory Concepts in Network Analysis
Favicon
AGE PG15/16 New updates
Favicon
Does your APP need Apache AGE?
Favicon
10 Reasons Why to use Apache AGE alongside PostgreSQL
Favicon
Apache AGE Complete Installation Guide - Part 3 and Last ( AGE )
Favicon
Apache AGE Complete Installation Guide - Part 2 ( PostgreSQL )
Favicon
Importing graph from files
Favicon
Introduction to Apache AGE: Exploring the Capabilities
Favicon
How to create and plot graphs in Python
Favicon
AWS Neptune for analysing event ticket sales between users - Part 1
Favicon
Apache AGE, Why you should use it
Favicon
Unleashing the Power of Data Analytics with Apache AGE: The Synergy of Graph Databases and Machine Learning - Part 1
Favicon
Just a super easy flowchart
Favicon
The Power of Graph Databases: Unlocking the Potential of Connected Data
Favicon
How to read a histogram?

Featured ones: