Master advanced graph operations including adjacency matrix, adjacency list, and various graph representations.
class GraphMatrix:
def __init__(self, num_vertices):
self.num_vertices = num_vertices
self.matrix = [[0 for _ in range(num_vertices)] for _ in range(num_vertices)]
def add_edge(self, source, destination, weight=1):
# For directed graph
self.matrix[source][destination] = weight
# For undirected graph, uncomment the line below
# self.matrix[destination][source] = weight
def remove_edge(self, source, destination):
self.matrix[source][destination] = 0
# For undirected graph, uncomment the line below
# self.matrix[destination][source] = 0
def has_edge(self, source, destination):
return self.matrix[source][destination] != 0
def get_neighbors(self, vertex):
neighbors = []
for i in range(self.num_vertices):
if self.matrix[vertex][i] != 0:
neighbors.append(i)
return neighbors
class GraphList:
def __init__(self):
self.adjacency_list = {}
def add_vertex(self, vertex):
if vertex not in self.adjacency_list:
self.adjacency_list[vertex] = []
def add_edge(self, source, destination, weight=1):
if source not in self.adjacency_list:
self.add_vertex(source)
if destination not in self.adjacency_list:
self.add_vertex(destination)
# For weighted graph, store dicts with node and weight
self.adjacency_list[source].append({'node': destination, 'weight': weight})
# For undirected graph, uncomment the line below
# self.adjacency_list[destination].append({'node': source, 'weight': weight})
def remove_edge(self, source, destination):
if source in self.adjacency_list:
self.adjacency_list[source] = [edge for edge in self.adjacency_list[source] if edge['node'] != destination]
# For undirected graph, uncomment the block below
# if destination in self.adjacency_list:
# self.adjacency_list[destination] = [edge for edge in self.adjacency_list[destination] if edge['node'] != source]
def get_neighbors(self, vertex):
return [edge['node'] for edge in self.adjacency_list.get(vertex, [])]
Operation | Adjacency Matrix | Adjacency List |
---|---|---|
Add Edge | O(1) | O(1) |
Remove Edge | O(1) | O(E) |
Check Edge | O(1) | O(E) |
Get All Edges | O(V²) | O(V+E) |
Implement a function that converts an adjacency matrix to an adjacency list representation.
# Input adjacency matrix
matrix = [
[0, 1, 0, 1],
[1, 0, 1, 0],
[0, 1, 0, 1],
[1, 0, 1, 0]
]
# Expected output adjacency list
adjacency_list = {
'0': [1, 3],
'1': [0, 2],
'2': [1, 3],
'3': [0, 2]
}
def matrix_to_list(matrix):
adj_list = {}
for i, row in enumerate(matrix):
adj_list[str(i)] = [j for j, val in enumerate(row) if val != 0]
return adj_list
print(matrix_to_list(matrix))
Implement a function that returns all vertices reachable from a given vertex in a directed graph.
Implement a function that calculates the density of a graph (ratio of actual edges to maximum possible edges).
For a directed graph with V vertices:
Density = Number of Edges / (V * (V-1))
For an undirected graph:
Density = (2 * Number of Edges) / (V * (V-1))
Note: Previously, this course referenced the CodeSignal Arcade for practice, which is no longer available. The LeetCode problems below follow the same principles and are excellent for practicing graph representations and algorithms.