A graph is a representation of a network structure. There are tons of graph real world examples, the Internet and the social graph being the classic ones.

It’s basically a set of nodes connected by edges.

I’ll skip the mathematical concepts since you can find them everywhere and jump directly to a Go implementation of a graph.

## Implementation

A graph data structure will expose those methods:

• AddNode() inserts a node
• AddEdge() adds an edge between two nodes

and String() for inspection purposes.

A Graph is defined as

type ItemGraph struct {
nodes []*Node
edges map[Node][]*Node
lock  sync.RWMutex
}

with Node being

type Node struct {
value Item
}

I’ll implement an undirected graph, which means that adding an edge from A to B also adds an edge from B to A.

## Implementation

// Package graph creates a ItemGraph data structure for the Item type
package graph

import (
"fmt"
"sync"

"github.com/cheekybits/genny/generic"
)

// Item the type of the binary search tree
type Item generic.Type

// Node a single node that composes the tree
type Node struct {
value Item
}

func (n *Node) String() string {
return fmt.Sprintf("%v", n.value)
}

// ItemGraph the Items graph
type ItemGraph struct {
nodes []*Node
edges map[Node][]*Node
lock  sync.RWMutex
}

func (g *ItemGraph) AddNode(n *Node) {
g.lock.Lock()
g.nodes = append(g.nodes, n)
g.lock.Unlock()
}

func (g *ItemGraph) AddEdge(n1, n2 *Node) {
g.lock.Lock()
if g.edges == nil {
g.edges = make(map[Node][]*Node)
}
g.edges[*n1] = append(g.edges[*n1], n2)
g.edges[*n2] = append(g.edges[*n2], n1)
g.lock.Unlock()
}

func (g *ItemGraph) String() {
g.lock.RLock()
s := ""
for i := 0; i < len(g.nodes); i++ {
s += g.nodes[i].String() + " -> "
near := g.edges[*g.nodes[i]]
for j := 0; j < len(near); j++ {
s += near[j].String() + " "
}
s += "\n"
}
fmt.Println(s)
g.lock.RUnlock()
}

Quite straightforward. Here’s a test that when run, will populate the graph and print

\$ go test
A -> B C D
B -> A E
C -> A E
D -> A
E -> B C F
F -> E

PASS
package graph

import (
"fmt"
"testing"
)

var g ItemGraph

func fillGraph() {
nA := Node{"A"}
nB := Node{"B"}
nC := Node{"C"}
nD := Node{"D"}
nE := Node{"E"}
nF := Node{"F"}

}

fillGraph()
g.String()
}

## Traversing the graph: BFS

BFS (Breadth-First Search) is one of the most widely known algorithm to traverse a graph. Starting from a node, it first traverses all its directly linked nodes, then processes the nodes linked to those, and so on.

It’s implemented using a queue, which is generated from my Go implementation of a queue data structure with code generation for the node type:

// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny
package graph

import "sync"

// NodeQueue the queue of Nodes
type NodeQueue struct {
items []Node
lock  sync.RWMutex
}

// New creates a new NodeQueue
func (s *NodeQueue) New() *NodeQueue {
s.lock.Lock()
s.items = []Node{}
s.lock.Unlock()
return s
}

// Enqueue adds an Node to the end of the queue
func (s *NodeQueue) Enqueue(t Node) {
s.lock.Lock()
s.items = append(s.items, t)
s.lock.Unlock()
}

// Dequeue removes an Node from the start of the queue
func (s *NodeQueue) Dequeue() *Node {
s.lock.Lock()
item := s.items
s.items = s.items[1:len(s.items)]
s.lock.Unlock()
return &item
}

// Front returns the item next in the queue, without removing it
func (s *NodeQueue) Front() *Node {
s.lock.RLock()
item := s.items
s.lock.RUnlock()
return &item
}

// IsEmpty returns true if the queue is empty
func (s *NodeQueue) IsEmpty() bool {
s.lock.RLock()
defer s.lock.RUnlock()
return len(s.items) == 0
}

// Size returns the number of Nodes in the queue
func (s *NodeQueue) Size() int {
s.lock.RLock()
defer s.lock.RUnlock()
return len(s.items)
}

### Traverse method

Here’s the BFS implementation:

// Traverse implements the BFS traversing algorithm
func (g *ItemGraph) Traverse(f func(*Node)) {
g.lock.RLock()
q := NodeQueue{}
q.New()
n := g.nodes
q.Enqueue(*n)
visited := make(map[*Node]bool)
for {
if q.IsEmpty() {
break
}
node := q.Dequeue()
visited[node] = true
near := g.edges[*node]

for i := 0; i < len(near); i++ {
j := near[i]
if !visited[j] {
q.Enqueue(*j)
visited[j] = true
}
}
if f != nil {
f(node)
}
}
g.lock.RUnlock()
}

which can be tested with

func TestTraverse(t *testing.T) {
g.Traverse(func(n *Node) {
fmt.Printf("%v\n", n)
})
}

which after being added to our tests, will print the road it took to traverse all our nodes:

A
B
C
D
A
E
F

## Creating a concrete graph data structure

You can use this generic implemenation to generate type-specific graphs, using

//generate a IntGraph graph of int values
genny -in graph.go -out graph-int.go gen "Item=int"

//generate a StringGraph graph of string values
genny -in graph.go -out graph-string.go gen "Item=string"

##### Coming soon:
Huge Black Friday sale for all my courses