Graphs
Understanding graphs as a data structure.
What are Graphs?
A graph is a non-linear data structure consisting of vertices (nodes) and edges that connect these vertices. Unlike trees, which have a strict hierarchical structure, graphs can represent more complex relationships where connections can form cycles and nodes can have multiple paths between them.
Types of Graphs
-
Directed vs Undirected
- Directed (Digraph): Edges have a direction (one-way connection)
- Undirected: Edges have no direction (two-way connection)
-
Weighted vs Unweighted
- Weighted: Edges have associated costs or weights
- Unweighted: All edges have equal importance
-
Special Types
- Complete Graph: Every vertex is connected to every other vertex
- Bipartite Graph: Vertices can be divided into two sets with edges only between sets
- Tree: Connected graph with no cycles
- DAG (Directed Acyclic Graph): Directed graph with no cycles
Graph Representations
-
Adjacency Matrix
- 2D array where
matrix[i][j]
represents edge between verticesi
andj
- Space Complexity:
O(V²)
- Good for dense graphs
- 2D array where
-
Adjacency List
- Array/List of lists where each index stores connected vertices
- Space Complexity:
O(V + E)
- Better for sparse graphs
Common Graph Operations
-
Basic Operations
- Add vertex:
O(1)
- Add edge:
O(1)
for adjacency list,O(1)
for matrix - Remove vertex:
O(V + E)
for list,O(V²)
for matrix - Remove edge:
O(E)
for list,O(1)
for matrix - Check if edge exists:
O(V)
for list,O(1)
for matrix
- Add vertex:
-
Traversal Operations
- Depth First Search (DFS):
O(V + E)
- Breadth First Search (BFS):
O(V + E)
- Depth First Search (DFS):
Code Examples
Here's how you implement a basic graph in different languages:
from collections import defaultdict, deque
class Graph:
def __init__(self):
self.graph = defaultdict(list)
def add_edge(self, u, v):
# Add edge for undirected graph
self.graph[u].append(v)
self.graph[v].append(u)
def bfs(self, start):
visited = set()
queue = deque([start])
visited.add(start)
while queue:
vertex = queue.popleft()
print(vertex, end=" ")
for neighbor in self.graph[vertex]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
# Create the graph
g = Graph()
# Add edges
g.add_edge(0, 1)
g.add_edge(0, 2)
g.add_edge(1, 3)
g.add_edge(2, 4)
print("BFS starting from vertex 0: ", end="")
g.bfs(0) # Perform BFS starting from vertex 0
Common Graph Algorithms
-
Depth-First Search (DFS)
-
Breadth-First Search (BFS)
-
Dijkstra's Algorithm
-
Bellman-Ford Algorithm
-
Floyd-Warshall Algorithm
-
Kruskal's Algorithm
-
Prim's Algorithm
-
Topological Sort
-
Articulation Points