Graph Data Structures

🎨 Mastering Graphs: A Comprehensive Guide to Graph Data Structures 🔗

Graphs are an essential data structure that plays a pivotal role in solving complex problems ranging from social networks to web crawling and even route optimization. If you’ve ever wondered how your GPS finds the shortest path or how Google ranks pages, graphs are at work behind the scenes! Let’s dive into the world of graphs, exploring their types, representations, and algorithms with examples.

An-Example-Graph-Data-Structure-a-An-Example-Graph-with-8-vertices-and-17-edges-b


✨ What is a Graph?

A graph is a collection of nodes (also called vertices) connected by edges. It can represent various real-world relationships, such as connections on a social network, roads on a map, or dependencies in project tasks.

  • Vertices: The entities (e.g., cities, people, or webpages).
  • Edges: The relationships or connections (e.g., roads, friendships, or hyperlinks).

🔮 Types of Graphs

Graphs can be categorized based on their structure and properties. Let’s explore the main types:

1. Directed vs. Undirected Graphs

  • Directed Graphs (→): Each edge has a direction, pointing from one vertex to another.
    • Example: Representing follow relationships on Twitter.
    A → B
    
  • Undirected Graphs (↔): Edges have no direction, allowing movement both ways.
    • Example: Representing friendships on Facebook.
    A ↔ B
    

2. Weighted vs. Unweighted Graphs

  • Weighted Graphs: Each edge has a weight or cost associated with it.
    • Example: Representing distances between cities.
    A --- (5) --- B
    
  • Unweighted Graphs: All edges are considered equal.
    • Example: Representing a binary connection (e.g., is a connection present or not).

3. Cyclic vs. Acyclic Graphs

  • Cyclic Graphs: Contain at least one cycle (a path that starts and ends at the same vertex).
    • Example: Representing networks with feedback loops.
    A → B → C → A
    
  • Acyclic Graphs: No cycles exist.
    • Example: Representing dependencies in a project (Directed Acyclic Graph or DAG).
    A → B → C
    

4. Connected vs. Disconnected Graphs

  • Connected Graphs: All vertices are reachable from every other vertex.
    • Example: Representing a single group of friends in a social network.
  • Disconnected Graphs: Some vertices are isolated or not reachable from others.
    • Example: Representing multiple disconnected social groups.

🌐 Graph Representation Approaches

Graphs can be represented in several ways depending on the problem’s needs:

1. Adjacency Matrix

A 2D matrix of size V×V (where V is the number of vertices):

  • 1 indicates the presence of an edge, and 0 indicates no edge.
  • For weighted graphs, the matrix contains weights instead of 1s.

Example for an undirected graph:

  A B C
A 0 1 1
B 1 0 0
C 1 0 0

2. Adjacency List

Each vertex has a list of all adjacent vertices. Efficient for sparse graphs.

Example:

A -> B, C
B -> A
C -> A

3. Edge List

A list of all edges in the graph:

(A, B)
(A, C)

⚡ Common Graph Algorithms with Examples

1. Depth-First Search (DFS)

Explores as far as possible along each branch before backtracking.

How It Works:

  1. Start at a given node and mark it as visited.
  2. Recursively visit all its unvisited neighbors.

Example (Finding a path in a maze):

# DFS Implementation in Ruby
visited = {}
def dfs(graph, node)
  return if visited[node]
  visited[node] = true
  puts node
  graph[node].each { |neighbor| dfs(graph, neighbor) }
end

2. Breadth-First Search (BFS)

Explores all neighbors at the current depth before moving deeper.

How It Works:

  1. Start at a given node and enqueue it.
  2. Dequeue a node, mark it as visited, and enqueue its unvisited neighbors.

Example (Finding the shortest path):

# BFS Implementation in Ruby
require 'queue'
def bfs(graph, start)
  queue = [start]
  visited = {}
  visited[start] = true
  until queue.empty?
    node = queue.shift
    puts node
    graph[node].each do |neighbor|
      next if visited[neighbor]
      visited[neighbor] = true
      queue.push(neighbor)
    end
  end
end

3. Dijkstra’s Algorithm

Finds the shortest path from a source to all vertices in a weighted graph.

How It Works:

  1. Assign a tentative distance to every vertex (0 for the source, infinity for others).
  2. Pick the vertex with the smallest tentative distance and update its neighbors.
  3. Repeat until all vertices are visited.

Example:

# Dijkstra's Algorithm in Ruby
require 'priority_queue'
def dijkstra(graph, source)
  distances = Hash.new(Float::INFINITY)
  distances[source] = 0
  pq = PriorityQueue.new
  pq.push(source, 0)
  until pq.empty?
    current = pq.pop
    graph[current].each do |neighbor, weight|
      new_dist = distances[current] + weight
      if new_dist < distances[neighbor]
        distances[neighbor] = new_dist
        pq.push(neighbor, new_dist)
      end
    end
  end
  distances
end

4. Kruskal’s Algorithm

Finds the minimum spanning tree of a graph.

How It Works:

  1. Sort all edges by weight.
  2. Add edges one by one, ensuring no cycles are formed.

Example:

# Kruskal's Algorithm in Ruby
def kruskal(edges, vertices)
  edges.sort_by! { |(_, _, weight)| weight }
  parent = {}
  vertices.each { |v| parent[v] = v }

  def find(v, parent)
    return v if parent[v] == v
    parent[v] = find(parent[v], parent)
  end

  mst = []
  edges.each do |u, v, weight|
    root_u = find(u, parent)
    root_v = find(v, parent)
    next if root_u == root_v

    mst << [u, v, weight]
    parent[root_u] = root_v
  end
  mst
end

5. Bellman-Ford Algorithm

Finds shortest paths from a source to all vertices, even with negative edge weights.

How It Works:

  1. Relax all edges V-1 times.
  2. Check for negative weight cycles by relaxing edges one more time.

🚀 Real-World Applications of Graphs

  1. Social Networks: Representing friendships or followerships.
  2. Navigation Systems: Finding shortest paths (e.g., Google Maps).
  3. Recommendation Systems: Suggesting friends or products.
  4. Web Crawling: Representing the structure of websites.
  5. Network Routing: Optimizing data packet delivery.

🎉 Conclusion

Graphs are a powerful and versatile data structure with endless possibilities. Whether you’re building a recommendation engine or optimizing logistics, mastering graphs opens up a new realm of problem-solving opportunities.

What graph problem will you tackle today? 🤖

© Lakhveer Singh Rajput - Blogs. All Rights Reserved.