Welcome to our session on Graph Algorithms Implementation. A large proportion of real-world problems, from social networking to routing applications, can be represented by graphs. Understanding and implementing graph algorithms is thus a key skill to have in your programming toolkit. In this lesson, we introduce and explore one of the most fundamental graph traversal algorithms — the Breadth-First Search (BFS
).
A graph consists of nodes
connected by edges
. An adjacency list is a way of representing a graph as a collection of lists. In an adjacency list, each vertex u
in the graph has a list that contains all of the vertices v
that are adjacent to u
. Here's a breakdown of how it works:
- Vertices: Each vertex in the graph has a corresponding list.
- Edges: If there is an edge between vertices
u
andv
, then vertexv
will appear in the list for vertexu
, and vice versa for an undirected graph.
For example:
Plain text10 -> {1, 2} 21 -> {0} 32 -> {0, 3} 43 -> {2}
corresponds to this graph:
Plain text1 0 2 / \ 3 1 2 - 3
The Graph
structure we will use is:
Go1package main 2 3type Graph struct { 4 adjList map[int][]int 5} 6 7func NewGraph() *Graph { 8 return &Graph{adjList: make(map[int][]int)} 9} 10 11func (g *Graph) AddEdge(u, v int) { 12 g.adjList[u] = append(g.adjList[u], v) 13 g.adjList[v] = append(g.adjList[v], u) // Assuming an undirected graph 14} 15 16func (g *Graph) GetAdjList() map[int][]int { 17 return g.adjList 18}
In Go, you can use map[int][]int
to represent the adjacency list, where each key represents a vertex, and the value is a slice of integers representing adjacent vertices. This setup is efficient for lookups and insertions.
Let's take a sneak peek at the BFS
algorithm. Given a graph and a starting vertex, BFS
systematically explores the edges of the graph, visiting all neighbors of a vertex before moving on to the next level. It does this by managing a queue of vertices yet to be explored and consistently visiting all vertices adjacent to the current one before moving on.
A queue is a linear data structure that follows the First-In-First-Out (FIFO) principle. It means that the first element added to the queue will be the first one to be removed.
For this graph:
Plain text1 0 2 / \ 3 1 2 4 / \ \ 5 3 4 5
Running BFS
starting at node 0
will visit: 0 -> 1 -> 2 -> 3 -> 4 -> 5
The algorithm for BFS
is:
-
Initialization:
- Start with an initial node (
start
). - Mark
start
as visited. - Initialize a queue with
start
.
- Start with an initial node (
-
Traversal:
- While the queue is not empty:
- Dequeue the front node from the queue.
- Add all its unvisited neighbors to the queue.
- Mark each of these neighbors as visited to avoid processing them again.
- Add the dequeued node to the result list.
- While the queue is not empty:
-
Completion:
- The algorithm completes when the queue is empty, meaning all nodes that can be reached from the starting node have been visited in level-order fashion.
Here's the implementation of this BFS
algorithm:
Go1package main 2 3import "fmt" 4 5func bfs(graph *Graph, start int) []int { 6 visited := make(map[int]bool) 7 queue := []int{start} 8 result := []int{} 9 10 visited[start] = true 11 12 for len(queue) > 0 { 13 node := queue[0] 14 queue = queue[1:] 15 16 result = append(result, node) 17 for _, neighbor := range graph.GetAdjList()[node] { 18 if !visited[neighbor] { 19 visited[neighbor] = true 20 queue = append(queue, neighbor) 21 } 22 } 23 } 24 25 return result 26} 27 28func main() { 29 graph := NewGraph() 30 graph.AddEdge(0, 1) 31 graph.AddEdge(0, 2) 32 graph.AddEdge(1, 3) 33 graph.AddEdge(1, 4) 34 graph.AddEdge(2, 5) 35 36 traversal := bfs(graph, 0) 37 for _, node := range traversal { 38 fmt.Print(node, " ") // Output: 0 1 2 3 4 5 39 } 40}
As we delve into this session, we will understand the mechanics behind BFS
. Our study will include the concepts of traversal, the queue data structure's usefulness in BFS
, and how to handle the discovery and processing of nodes while ensuring all operations are efficiently handled using Go’s slices and maps. Equipped with these fundamentals, we'll practice a variety of problems calling for BFS
to perform node-level searches or find connected components in a graph. Let's dive in and uncover the power of graph algorithms!