@@ -0,0 +1,97 @@
|
||||
package graphs
|
||||
|
||||
import "slices"
|
||||
|
||||
// DAG -> Directed Acyclic Graph. Any edge that would introduce a cycle is
|
||||
// rejected, so the acyclic invariant always holds.
|
||||
type DAG[T comparable] struct {
|
||||
adjList map[T][]T
|
||||
}
|
||||
|
||||
// NewDAG() -> Creates a Directed Acyclic Graph
|
||||
func NewDAG[T comparable]() *DAG[T] {
|
||||
gp := &DAG[T]{
|
||||
adjList: make(map[T][]T),
|
||||
}
|
||||
|
||||
return gp
|
||||
}
|
||||
|
||||
// AddIsolatedVertex() -> Creates an isolated Vertex returns True if ok ,
|
||||
// false if vertex already exist
|
||||
func (gp *DAG[T]) AddIsolatedVertex(newVertex T) bool {
|
||||
_, ok := gp.adjList[newVertex]
|
||||
if !ok {
|
||||
gp.adjList[newVertex] = make([]T, 0)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AddVertex() -> adds a directed edge from -> to, creating either vertex if it
|
||||
// is missing. Returns false (and adds nothing) when the edge is a self loop,
|
||||
// already exists, or would create a cycle.
|
||||
func (gp *DAG[T]) AddVertex(from T, to T) bool {
|
||||
// self loops are cycles of length 1
|
||||
if from == to {
|
||||
return false
|
||||
}
|
||||
|
||||
gp.AddIsolatedVertex(from)
|
||||
gp.AddIsolatedVertex(to)
|
||||
|
||||
if slices.Contains(gp.adjList[from], to) {
|
||||
return false
|
||||
}
|
||||
|
||||
// from -> to closes a cycle iff "to" can already reach "from".
|
||||
if gp.hasPath(to, from) {
|
||||
return false
|
||||
}
|
||||
|
||||
gp.adjList[from] = append(gp.adjList[from], to)
|
||||
return true
|
||||
}
|
||||
|
||||
// hasPath() -> reports whether a directed path exists from src to dst.
|
||||
func (gp *DAG[T]) hasPath(src T, dst T) bool {
|
||||
visited := make(map[T]bool)
|
||||
return gp.dfs(src, dst, visited)
|
||||
}
|
||||
|
||||
func (gp *DAG[T]) dfs(current T, dst T, visited map[T]bool) bool {
|
||||
if current == dst {
|
||||
return true
|
||||
}
|
||||
|
||||
visited[current] = true
|
||||
|
||||
for _, next := range gp.adjList[current] {
|
||||
if !visited[next] && gp.dfs(next, dst, visited) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Order() -> returns the amount of Vertices
|
||||
func (gp *DAG[T]) Order() int {
|
||||
return len(gp.adjList)
|
||||
}
|
||||
|
||||
// Size() -> returns the amount of Edges
|
||||
func (gp *DAG[T]) Size() int {
|
||||
var size int
|
||||
|
||||
// directed: each edge is stored once, so no halving.
|
||||
for _, val := range gp.adjList {
|
||||
size += len(val)
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
// Display() -> returns adjList[map]T []T
|
||||
func (gp *DAG[T]) Display() map[T][]T {
|
||||
return gp.adjList
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package graphs
|
||||
|
||||
import "slices"
|
||||
|
||||
// DirectedGraph
|
||||
type DirectedGraph[T comparable] struct {
|
||||
adjList map[T][]T
|
||||
}
|
||||
|
||||
// NewDirectedGraph() -> Creates a Directed Graph
|
||||
func NewDirectedGraph[T comparable]() *DirectedGraph[T] {
|
||||
gp := &DirectedGraph[T]{
|
||||
adjList: make(map[T][]T),
|
||||
}
|
||||
|
||||
return gp
|
||||
}
|
||||
|
||||
// AddIsolatedVertex() -> Creates an isolated Vertex returns True if ok ,
|
||||
// false if vertex already exist
|
||||
func (gp *DirectedGraph[T]) AddIsolatedVertex(newVertex T) bool {
|
||||
_, ok := gp.adjList[newVertex]
|
||||
if !ok {
|
||||
gp.adjList[newVertex] = make([]T, 0)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AddVertex() -> Creates a directed edge newVertex -> toVertex. Creates both
|
||||
// vertices if they dont exist.
|
||||
func (gp *DirectedGraph[T]) AddVertex(newVertex T, toVertex T) {
|
||||
// self loops are not allowed in this graph
|
||||
if newVertex == toVertex {
|
||||
return
|
||||
}
|
||||
|
||||
gp.AddIsolatedVertex(newVertex)
|
||||
gp.AddIsolatedVertex(toVertex)
|
||||
|
||||
// directed: only store the edge on the source's out-list, once.
|
||||
if !slices.Contains(gp.adjList[newVertex], toVertex) {
|
||||
gp.adjList[newVertex] = append(gp.adjList[newVertex], toVertex)
|
||||
}
|
||||
}
|
||||
|
||||
// Order() -> returns the amount of Vertices
|
||||
func (gp *DirectedGraph[T]) Order() int {
|
||||
return len(gp.adjList)
|
||||
}
|
||||
|
||||
// Size() -> returns the amount of Edges
|
||||
func (gp *DirectedGraph[T]) Size() int {
|
||||
var size int
|
||||
|
||||
// directed: each edge is stored once, so no halving.
|
||||
for _, val := range gp.adjList {
|
||||
size += len(val)
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
// Display() -> returns adjList[map]T []T
|
||||
func (gp *DirectedGraph[T]) Display() map[T][]T {
|
||||
return gp.adjList
|
||||
}
|
||||
Reference in New Issue
Block a user