A Directed Acyclic Graph (DAG) is a graph representing a structure formed by vertices, or nodes, connected by directed edges. In a DAG, each edge has an initial vertex, called the tail, and a final vertex, called the head. The graph is considered acyclic because it does not contain any cycles, meaning there are no sequences of consecutive directed edges that form a closed loop.
DAGs have wide-ranging applications, such as representing dependencies in build systems, scheduling tasks with precedence constraints, and even modeling blockchain structures in some modern cryptocurrencies.
In this blog post, we'll build a simple DAG implementation in Golang and explore some potential use cases.
We'll start by defining a struct DAG
that will store vertices and directed edges:
package main import "fmt" type Vertex struct { ID string children []*Vertex } type DAG struct { vertices map[string]*Vertex } func NewDAG() *DAG { return &DAG{vertices: make(map[string]*Vertex)} } func (d *DAG) AddVertex(id string) { vertex := &Vertex{ ID: id, children: make([]*Vertex, 0), } d.vertices[id] = vertex } func (d *DAG) AddEdge(head string, tail string) error { headVertex, headExists := d.vertices[head] tailVertex, tailExists := d.vertices[tail] if !headExists || !tailExists { return fmt.Errorf("both vertices must exist") } headVertex.children = append(headVertex.children, tailVertex) return nil }
Now, we have a basic structure with two methods, AddVertex
and AddEdge
. The AddVertex
method creates a new vertex and adds it to the DAG
object, while the AddEdge
method connects vertices by adding an edge from the head
to the tail
.
Let's create a simple DAG with four vertices, representing four tasks that need to be completed in a certain order:
func main() { dag := NewDAG() dag.AddVertex("1. Write code") dag.AddVertex("2. Test code") dag.AddVertex("3. Debug code") dag.AddVertex("4. Deploy code") dag.AddEdge("1. Write code", "2. Test code") dag.AddEdge("2. Test code", "3. Debug code") dag.AddEdge("3. Debug code", "4. Deploy code") fmt.Println("Vertices:") for _, vertex := range dag.vertices { fmt.Println(vertex.ID) } fmt.Println("\nEdges:") for _, vertex := range dag.vertices { for _, child := range vertex.children { fmt.Printf("%v -> %v\n", vertex.ID, child.ID) } } }
Vertices: 1. Write code 2. Test code 3. Debug code 4. Deploy code Edges: 1. Write code -> 2. Test code 2. Test code -> 3. Debug code 3. Debug code -> 4. Deploy code
Now, we have a simple DAG with vertices and edges that represent the task sequence.
In this blog post, we presented a simple Golang implementation of a Directed Acyclic Graph (DAG) and demonstrated its potential use in representing task sequences. DAGs are fundamental structures with numerous applications across different fields, and this basic implementation can be extended to accommodate more complex scenarios and applications.