Graphs

A graph is a non-linear data structure consisting of nodes (vertices) and edges that connect them. Graphs are fundamental in computer science, modeling relationships in social networks, transportation systems, computer networks, and countless other applications.

Interactive Graph Visualization

Explore graph traversal algorithms with our interactive visualizer. Create custom graphs, drag nodes around, and watch BFS and DFS algorithms traverse the graph step-by-step with color-coded animations.

Graph Operations & Traversal Algorithms

Add Node

Create a new vertex

Add Edge

Connect two vertices

Graph Traversal

Animate traversal algorithms

800ms

Interactive Graph Visualization

Default
Current
In Queue (BFS)
In Stack (DFS)
Backtracking
Visited

Drag nodes to reposition • Click to deselect • 4 nodes, 3 edges

Graph Traversal Algorithms

Breadth-First Search (BFS):

Explores all neighbors at the current depth before moving to nodes at the next depth level. Uses a queue data structure and guarantees the shortest path in unweighted graphs.

Time: O(V + E), Space: O(V)

Depth-First Search (DFS):

Explores as far as possible along each branch before backtracking. Uses recursion (or stack) and is useful for detecting cycles and finding connected components.

Time: O(V + E), Space: O(V)

Graph Fundamentals

Core Components

  • Vertices (Nodes): Individual entities in the graph
  • Edges (Links): Connections between vertices
  • Degree: Number of edges connected to a vertex
  • Path: Sequence of vertices connected by edges
  • Cycle: Path that starts and ends at the same vertex

Graph Properties

  • Connected: Path exists between any two vertices
  • Sparse vs Dense: Few edges vs many edges
  • Planar: Can be drawn without edge crossings
  • Bipartite: Vertices can be divided into two disjoint sets
  • Complete: Every vertex connected to every other vertex

Types of Graphs

Directed vs Undirected

Directed: Edges have direction (A → B)

Undirected: Edges are bidirectional (A ↔ B)

Weighted vs Unweighted

Weighted: Edges have associated costs/distances

Unweighted: All edges have equal value

Cyclic vs Acyclic

Cyclic: Contains at least one cycle

Acyclic: No cycles (e.g., trees, DAGs)

Connected vs Disconnected

Connected: Path exists between all vertices

Disconnected: Some vertices are unreachable

Graph Representation Methods

Adjacency Matrix

2D array where matrix[i][j] represents edge from vertex i to vertex j

   A B C
A [0 1 1]
B [1 0 1]
C [1 1 0]
  • Space: O(V²) - always uses V² space
  • Edge Check: O(1) - direct array access
  • Add Edge: O(1) - set matrix value
  • Get Neighbors: O(V) - scan entire row
  • Best for: Dense graphs, frequent edge queries

Adjacency List ⭐

Array/Map of lists where each vertex stores its neighbors

A: [B, C]
B: [A, C]
C: [A, B]
  • Space: O(V + E) - only stores existing edges
  • Edge Check: O(degree) - search neighbor list
  • Add Edge: O(1) - append to list
  • Get Neighbors: O(degree) - return list
  • Best for: Sparse graphs, traversal algorithms

💡 Recommendation: Adjacency List is often more efficient for sparse graphs (few edges relative to vertices), which describes most real-world graphs. It's the preferred choice for traversal algorithms like BFS and DFS.

Graph Traversal Algorithms

Breadth-First Search (BFS)

Algorithm:

  1. Start with a queue containing the start vertex
  2. Mark the start vertex as visited
  3. While queue is not empty:
  4. • Dequeue a vertex and process it
  5. • Add all unvisited neighbors to queue
  6. • Mark neighbors as visited

Time Complexity: O(V + E)

Space Complexity: O(V) for queue and visited set

Properties: Explores level by level, finds shortest path in unweighted graphs

Applications: Shortest path, social networks, web crawling

Depth-First Search (DFS)

Algorithm:

  1. Start with a stack containing the start vertex
  2. While stack is not empty:
  3. • Pop a vertex from stack
  4. • If not visited, mark as visited and process
  5. • Add all unvisited neighbors to stack
  6. Alternative: Use recursion (implicit stack)

Time Complexity: O(V + E)

Space Complexity: O(V) for stack and visited set

Properties: Explores as deep as possible before backtracking

Applications: Cycle detection, topological sorting, maze solving

Operation Complexity Analysis

Operation Adjacency Matrix Adjacency List Notes
Space O(V²) O(V + E) List is better for sparse graphs
Add Vertex O(V²) O(1) Matrix needs resizing
Add Edge O(1) O(1) Both constant time
Check Edge O(1) O(degree) Matrix has direct access
Get Neighbors O(V) O(degree) List stores only actual neighbors
BFS/DFS O(V²) O(V + E) List avoids checking non-edges

Graph Implementation & Traversal Algorithms

Complete graph implementations using adjacency lists with both BFS and DFS traversal algorithms. Study how the queue in BFS enables level-order traversal while the stack in DFS enables deep exploration.

from collections import defaultdict, deque

class Graph:
    """
    Graph implementation using Adjacency List representation
    Supports both directed and undirected graphs
    """
    
    def __init__(self, directed=False):
        self.graph = defaultdict(list)  # Adjacency list
        self.directed = directed
        self.vertices = set()
    
    def add_vertex(self, vertex):
        """Add a vertex to the graph"""
        self.vertices.add(vertex)
        if vertex not in self.graph:
            self.graph[vertex] = []
    
    def add_edge(self, u, v, weight=1):
        """
        Add an edge between vertices u and v
        For undirected graphs, add edge in both directions
        """
        self.vertices.add(u)
        self.vertices.add(v)
        
        # Add edge from u to v
        self.graph[u].append((v, weight))
        
        # For undirected graphs, add reverse edge
        if not self.directed:
            self.graph[v].append((u, weight))
    
    def get_neighbors(self, vertex):
        """Get all neighbors of a vertex"""
        return [neighbor for neighbor, weight in self.graph[vertex]]
    
    def print_graph(self):
        """Print the adjacency list representation"""
        for vertex in self.vertices:
            neighbors = self.get_neighbors(vertex)
            print(f"{vertex}: {neighbors}")
    
    def bfs(self, start_vertex):
        """
        Breadth-First Search traversal
        Time Complexity: O(V + E)
        Space Complexity: O(V)
        """
        if start_vertex not in self.vertices:
            return []
        
        visited = set()
        queue = deque([start_vertex])
        traversal_order = []
        
        visited.add(start_vertex)
        
        while queue:
            # Dequeue a vertex from front of queue
            current = queue.popleft()
            traversal_order.append(current)
            
            # Visit all unvisited neighbors
            for neighbor in self.get_neighbors(current):
                if neighbor not in visited:
                    visited.add(neighbor)
                    queue.append(neighbor)
        
        return traversal_order
    
    def dfs(self, start_vertex):
        """
        Depth-First Search traversal (iterative)
        Time Complexity: O(V + E)
        Space Complexity: O(V)
        """
        if start_vertex not in self.vertices:
            return []
        
        visited = set()
        stack = [start_vertex]
        traversal_order = []
        
        while stack:
            # Pop a vertex from top of stack
            current = stack.pop()
            
            if current not in visited:
                visited.add(current)
                traversal_order.append(current)
                
                # Add all unvisited neighbors to stack
                # Add in reverse order to maintain left-to-right traversal
                neighbors = self.get_neighbors(current)
                for neighbor in reversed(neighbors):
                    if neighbor not in visited:
                        stack.append(neighbor)
        
        return traversal_order
    
    def dfs_recursive(self, start_vertex, visited=None):
        """
        Depth-First Search traversal (recursive)
        Time Complexity: O(V + E)
        Space Complexity: O(V) - due to recursion stack
        """
        if visited is None:
            visited = set()
        
        if start_vertex not in self.vertices:
            return []
        
        visited.add(start_vertex)
        traversal_order = [start_vertex]
        
        # Visit all unvisited neighbors recursively
        for neighbor in self.get_neighbors(start_vertex):
            if neighbor not in visited:
                traversal_order.extend(
                    self.dfs_recursive(neighbor, visited)
                )
        
        return traversal_order

# Example usage and testing
if __name__ == "__main__":
    # Create an undirected graph
    g = Graph(directed=False)
    
    # Add vertices and edges
    g.add_edge('A', 'B')
    g.add_edge('A', 'C')
    g.add_edge('B', 'D')
    g.add_edge('C', 'E')
    g.add_edge('D', 'E')
    g.add_edge('E', 'F')
    
    print("Graph representation:")
    g.print_graph()
    
    print(f"\nBFS from A: {g.bfs('A')}")
    print(f"DFS from A: {g.dfs('A')}")
    print(f"DFS Recursive from A: {g.dfs_recursive('A')}")

Real-World Applications

Social Networks

Model friendships, connections, and information spread. BFS finds shortest social distance.

Transportation

GPS navigation, flight routes, public transit. Weighted graphs represent distances or travel time.

Computer Networks

Internet topology, routing protocols, network analysis. DFS detects network loops.

Web Crawling

Search engines use BFS to discover web pages level by level from seed URLs.

Game Development

Pathfinding in games, decision trees, state machines. A* algorithm extends BFS.

Dependency Resolution

Package managers, build systems, task scheduling. DFS enables topological sorting.

Advanced Graph Algorithms

Dijkstra's Algorithm: Shortest path in weighted graphs

Minimum Spanning Tree: Kruskal's and Prim's algorithms

Topological Sort: Ordering vertices in directed acyclic graphs

Strongly Connected Components: Finding cycles in directed graphs

Maximum Flow: Ford-Fulkerson algorithm for network flow problems