Data Structures Explained with Python

Graph Data Structures in Python

A graph is a non-linear data structure consisting of a set of vertices (or nodes) and a set of edges that connect pairs of vertices. Graphs are used to represent networks, relationships between objects, and various other real-world scenarios.

Key Terminologies:

Types of Graphs:

Graph Representations:

There are two common ways to represent graphs in memory:

  1. Adjacency Matrix: A 2D array where matrix[i][j] = 1 (or weight) if there is an edge from vertex i to vertex j, and 0 (or infinity) otherwise. Space complexity is O(V^2), where V is the number of vertices. Good for dense graphs.
  2. Adjacency List: An array of lists (or dictionaries/hash maps). For each vertex i, adj_list[i] stores a list of vertices adjacent to i. Space complexity is O(V+E) for undirected graphs and O(V+E) for directed graphs, where E is the number of edges. Good for sparse graphs.

Adjacency List Implementation in Python (using a dictionary):


class Graph:
    def __init__(self):
        self.adj_list = {}

    def add_vertex(self, vertex):
        if vertex not in self.adj_list:
            self.adj_list[vertex] = []

    # For an undirected graph
    def add_edge(self, v1, v2):
        if v1 in self.adj_list and v2 in self.adj_list:
            self.adj_list[v1].append(v2)
            self.adj_list[v2].append(v1) # For undirected, add both ways
        else:
            print("One or more vertices not found.")

    # For a directed graph
    def add_directed_edge(self, u, v):
        if u in self.adj_list and v in self.adj_list:
            self.adj_list[u].append(v)
        else:
            print("One or more vertices not found.")

    def print_graph(self):
        for vertex in self.adj_list:
            print(f"{vertex}: {self.adj_list[vertex]}")

# Example Usage (Undirected Graph):
g = Graph()
g.add_vertex('''A''')
g.add_vertex('''B''')
g.add_vertex('''C''')
g.add_vertex('''D''')

g.add_edge('''A''', '''B''')
g.add_edge('''A''', '''C''')
g.add_edge('''B''', '''D''')
g.add_edge('''C''', '''D''')

print("Undirected Graph Adjacency List:")
g.print_graph()
# Output:
# A: ['''B''', '''C''']
# B: ['''A''', '''D''']
# C: ['''A''', '''D''']
# D: ['''B''', '''C''']

# Example Usage (Directed Graph):
dg = Graph()
dg.add_vertex(0)
dg.add_vertex(1)
dg.add_vertex(2)
dg.add_vertex(3)

dg.add_directed_edge(0, 1)
dg.add_directed_edge(0, 2)
dg.add_directed_edge(1, 2)
dg.add_directed_edge(2, 0)
dg.add_directed_edge(2, 3)
dg.add_directed_edge(3, 3)

print("\nDirected Graph Adjacency List:")
dg.print_graph()
# Output:
# 0: [1, 2]
# 1: [2]
# 2: [0, 3]
# 3: [3]
        

Common Graph Algorithms:

Applications of Graphs: