Graph Data Structures

🌐 Networks & Connections: Webs of Relationships

Master the art of representing relationships and connections in data

1 Why Do We Need Graphs?

🌍 Real-World Problems

🗺️ Navigation Apps: Finding the shortest route between locations
👥 Social Networks: Analyzing friend connections and recommendations
🌐 Internet: Routing data between servers and websites
🧬 Biology: Modeling protein interactions and genetic networks

Network Everywhere!

From the neurons in your brain to the global internet, networks of connected elements are fundamental to how our world works. Graphs give us the tools to represent, analyze, and navigate these complex relationships.

2 Mental Model: Social Network Web

🕸️ Think of Graphs Like a Social Media Network

👤
Nodes (Vertices)

Like people in your social network - each has a unique identity

🤝
Edges (Connections)

Like friendships or follows - they connect people together

↔️
Undirected

Like mutual friendships - if A knows B, then B knows A

➡️
Directed

Like following on Twitter - A can follow B without B following back

🔄 Model Evolution: From Simple to Sophisticated

Level 1 Simple Network: Just people and friendships
Level 2 Weighted Network: Friendships have "strength" (close friends vs acquaintances)
Level 3 Complex Network: Multiple types of relationships (family, work, hobby groups)

3 Graph Representations in Python

📊 Adjacency List

Like a contact list where each person has a list of their friends

# Adjacency List - Dictionary of Lists
graph = {
    'Alice': ['Bob', 'Charlie'],
    'Bob': ['Alice', 'David'],
    'Charlie': ['Alice', 'David'],
    'David': ['Bob', 'Charlie', 'Eve'],
    'Eve': ['David']
}

# Check Alice's friends
print(graph['Alice'])  # ['Bob', 'Charlie']

📋 Adjacency Matrix

Like a friendship chart where 1 means "friends" and 0 means "not friends"

# Adjacency Matrix - 2D List
# People: Alice=0, Bob=1, Charlie=2, David=3, Eve=4
graph_matrix = [
    [0, 1, 1, 0, 0],  # Alice
    [1, 0, 0, 1, 0],  # Bob
    [1, 0, 0, 1, 0],  # Charlie
    [0, 1, 1, 0, 1],  # David
    [0, 0, 0, 1, 0]   # Eve
]

# Check if Alice (0) is friends with Bob (1)
print(graph_matrix[0][1])  # 1 (yes)

4 Hands-on Practice: Build Your Network

🎨 Graph Visualization

Add nodes and edges to build your graph!

📊 Adjacency List

{ }

🔍 Graph Analysis

Nodes: 0
Edges: 0
Density: 0%

5 Graph Traversal: Exploring the Network

🚶‍♂️ Breadth-First Search (BFS)

Like exploring your social network level by level - first your direct friends, then their friends, then their friends' friends

from collections import deque

def bfs(graph, start):
    visited = set()
    queue = deque([start])
    result = []
    
    while queue:
        node = queue.popleft()
        if node not in visited:
            visited.add(node)
            result.append(node)
            # Add neighbors to queue
            for neighbor in graph[node]:
                if neighbor not in visited:
                    queue.append(neighbor)
    
    return result

🏃‍♂️ Depth-First Search (DFS)

Like following one friendship chain as far as possible before backtracking

def dfs(graph, start, visited=None):
    if visited is None:
        visited = set()
    
    result = []
    if start not in visited:
        visited.add(start)
        result.append(start)
        
        for neighbor in graph[start]:
            result.extend(dfs(graph, neighbor, visited))
    
    return result

🎮 Interactive Traversal

Build a graph above and try traversal algorithms!

6 Critical Pitfall: Cycles and Infinite Loops

⚠️ The Infinite Loop Trap

Unlike trees, graphs can have cycles. Without proper tracking of visited nodes, your traversal can loop forever!

❌ Wrong: No Visited Tracking

# DANGER: This will loop forever in cyclic graphs!
def bad_dfs(graph, node):
    result = [node]
    for neighbor in graph[node]:
        result.extend(bad_dfs(graph, neighbor))  # Infinite recursion!
    return result

# With cycle A -> B -> A, this never stops!
💥 Result: Stack overflow from infinite recursion

✅ Right: Track Visited Nodes

# SAFE: Always track visited nodes
def safe_dfs(graph, node, visited=None):
    if visited is None:
        visited = set()
    
    if node in visited:  # Stop if already visited
        return []
    
    visited.add(node)
    result = [node]
    
    for neighbor in graph[node]:
        result.extend(safe_dfs(graph, neighbor, visited))
    
    return result
✅ Result: Safe traversal that terminates properly

7 Knowledge Assessment: Graph Mastery

Question 1: Graph Representation

Which representation is more memory-efficient for a sparse graph (few edges)?

Question 2: Traversal Order

For the graph A-B-C where A connects to B and C, what's the BFS order starting from A?

Question 3: Cycle Detection

Why is tracking visited nodes crucial in graph traversal?

Mastery Progress: 0%