Skip to content

Commit

Permalink
Iterate on graph (#32)
Browse files Browse the repository at this point in the history
* graph: refactoring
* graph: adding more visitor types
* graph: adding traversal for weighted graphs
* graph: refactoring visitor functions
  • Loading branch information
moorara authored May 18, 2020
1 parent 9d2eb5d commit 97209c2
Show file tree
Hide file tree
Showing 9 changed files with 543 additions and 283 deletions.
113 changes: 45 additions & 68 deletions ds/graph/directed.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,13 @@ func NewDirected(V int, edges ...[2]int) *Directed {
adj: adj,
}

for _, edge := range edges {
g.AddEdge(edge[0], edge[1])
for _, e := range edges {
g.AddEdge(e[0], e[1])
}

return g
}

func (g *Directed) isVertexValid(v int) bool {
return v >= 0 && v < g.v
}

// V returns the number of vertices.
func (g *Directed) V() int {
return g.v
Expand All @@ -49,12 +45,15 @@ func (g *Directed) E() int {
return g.e
}

func (g *Directed) isVertexValid(v int) bool {
return v >= 0 && v < g.v
}

// InDegree returns the number of directed edges incident to a vertex.
func (g *Directed) InDegree(v int) int {
if !g.isVertexValid(v) {
return -1
}

return g.ins[v]
}

Expand All @@ -63,10 +62,17 @@ func (g *Directed) OutDegree(v int) int {
if !g.isVertexValid(v) {
return -1
}

return len(g.adj[v])
}

// Adj returns the vertices adjacent from vertex.
func (g *Directed) Adj(v int) []int {
if !g.isVertexValid(v) {
return nil
}
return g.adj[v]
}

// AddEdge adds a new edge to the graph.
func (g *Directed) AddEdge(v, w int) {
if g.isVertexValid(v) && g.isVertexValid(w) {
Expand All @@ -76,15 +82,6 @@ func (g *Directed) AddEdge(v, w int) {
}
}

// Adj returns the vertices adjacent from vertex.
func (g *Directed) Adj(v int) []int {
if !g.isVertexValid(v) {
return nil
}

return g.adj[v]
}

// Reverse returns the reverse of the directed graph.
func (g *Directed) Reverse() *Directed {
rev := NewDirected(g.v)
Expand All @@ -98,125 +95,105 @@ func (g *Directed) Reverse() *Directed {
}

// DFS Traversal (Recursion)
func (g *Directed) _traverseDFS(visited []bool, v int, order TraverseOrder, visit VisitFunc) {
func (g *Directed) _traverseDFS(visited []bool, v int, order TraverseOrder, visitor *Visitor) {
visited[v] = true

if order == PreOrder {
visit(v)
if order == PreOrder && visitor != nil && visitor.VisitVertex != nil {
visitor.VisitVertex(v)
}

for _, w := range g.adj[v] {
if !visited[w] {
g._traverseDFS(visited, w, order, visit)
g._traverseDFS(visited, w, order, visitor)
}
}

if order == PostOrder {
visit(v)
if order == PostOrder && visitor != nil && visitor.VisitVertex != nil {
visitor.VisitVertex(v)
}
}

// DFS Traversal (Driver)
func (g *Directed) traverseDFS(s int, order TraverseOrder, visit VisitFunc) {
if !g.isVertexValid(s) {
return
}

if order != PreOrder && order != PostOrder {
return
}

func (g *Directed) traverseDFS(s int, order TraverseOrder, visitor *Visitor) {
visited := make([]bool, g.V())
g._traverseDFS(visited, s, order, visit)
g._traverseDFS(visited, s, order, visitor)
}

// Iterative DFS Traversal
func (g *Directed) traverseDFSIterative(s int, order TraverseOrder, visit VisitFunc) {
if !g.isVertexValid(s) {
return
}

if order != PreOrder && order != PostOrder {
return
}

func (g *Directed) traverseDFSIterative(s int, order TraverseOrder, visitor *Visitor) {
visited := make([]bool, g.V())
stack := list.NewStack(listNodeSize)

visited[s] = true
stack.Push(s)
if order == PreOrder {
visit(s)
if order == PreOrder && visitor != nil && visitor.VisitVertex != nil {
visitor.VisitVertex(s)
}

for !stack.IsEmpty() {
v := stack.Pop().(int)
if order == PostOrder {
visit(v)
if order == PostOrder && visitor != nil && visitor.VisitVertex != nil {
visitor.VisitVertex(v)
}

for _, w := range g.adj[v] {
if !visited[w] {
visited[w] = true
stack.Push(w)
if order == PreOrder {
visit(w)
if order == PreOrder && visitor != nil && visitor.VisitVertex != nil {
visitor.VisitVertex(w)
}
}
}
}
}

// BFS Traversal
func (g *Directed) traverseBFS(s int, order TraverseOrder, visit VisitFunc) {
if !g.isVertexValid(s) {
return
}

if order != PreOrder && order != PostOrder {
return
}

func (g *Directed) traverseBFS(s int, order TraverseOrder, visitor *Visitor) {
visited := make([]bool, g.V())
queue := list.NewQueue(listNodeSize)

visited[s] = true
queue.Enqueue(s)
if order == PreOrder {
visit(s)
if order == PreOrder && visitor != nil && visitor.VisitVertex != nil {
visitor.VisitVertex(s)
}

for !queue.IsEmpty() {
v := queue.Dequeue().(int)
if order == PostOrder {
visit(v)
if order == PostOrder && visitor != nil && visitor.VisitVertex != nil {
visitor.VisitVertex(v)
}

for _, w := range g.adj[v] {
if !visited[w] {
visited[w] = true
queue.Enqueue(w)
if order == PreOrder {
visit(w)
if order == PreOrder && visitor != nil && visitor.VisitVertex != nil {
visitor.VisitVertex(w)
}
}
}
}
}

// Traverse is used for visiting all vertices in graph.
func (g *Directed) Traverse(s int, strategy TraverseStrategy, order TraverseOrder, visit VisitFunc) {
if strategy != DFS && strategy != DFSIterative && strategy != BFS {
func (g *Directed) Traverse(s int, strategy TraverseStrategy, order TraverseOrder, visitor *Visitor) {
if !g.isVertexValid(s) {
return
}

if order != PreOrder && order != PostOrder {
return
}

switch strategy {
case DFS:
g.traverseDFS(s, order, visit)
g.traverseDFS(s, order, visitor)
case DFSIterative:
g.traverseDFSIterative(s, order, visit)
g.traverseDFSIterative(s, order, visitor)
case BFS:
g.traverseBFS(s, order, visit)
g.traverseBFS(s, order, visitor)
}
}

Expand Down
59 changes: 15 additions & 44 deletions ds/graph/directed_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,21 @@ func TestDirected(t *testing.T) {
},
},
traverseTests: []traverseTest{
{
name: "InvalidVertex",
start: -1,
expectedVisits: []int{},
},
{
name: "InvalidStrategy",
start: 0,
strategy: -1,
expectedVisits: []int{},
},
{
name: "InvalidOrderDFS",
start: 0,
strategy: DFS,
order: -1,
expectedVisits: []int{},
},
Expand All @@ -115,20 +126,6 @@ func TestDirected(t *testing.T) {
order: PostOrder,
expectedVisits: []int{1, 3, 2, 4, 5, 0},
},
{
name: "InvalidVertexDFS",
start: -1,
strategy: DFS,
order: PreOrder,
expectedVisits: []int{},
},
{
name: "InvalidOrderDFS",
start: 0,
strategy: DFS,
order: -1,
expectedVisits: []int{},
},
{
name: "PreOrderDFSIterative",
start: 0,
Expand All @@ -143,20 +140,6 @@ func TestDirected(t *testing.T) {
order: PostOrder,
expectedVisits: []int{0, 5, 4, 3, 2, 1},
},
{
name: "InvalidVertexDFSIterative",
start: -1,
strategy: DFSIterative,
order: PreOrder,
expectedVisits: []int{},
},
{
name: "InvalidOrderDFSIterative",
start: 0,
strategy: DFSIterative,
order: -1,
expectedVisits: []int{},
},
{
name: "PreOrderBFS",
start: 0,
Expand All @@ -171,20 +154,6 @@ func TestDirected(t *testing.T) {
order: PostOrder,
expectedVisits: []int{0, 1, 5, 4, 2, 3},
},
{
name: "InvalidVertexBFS",
start: -1,
strategy: BFS,
order: PreOrder,
expectedVisits: []int{},
},
{
name: "InvalidOrderBFS",
start: 0,
strategy: BFS,
order: -1,
expectedVisits: []int{},
},
},
},
}
Expand Down Expand Up @@ -217,8 +186,10 @@ func TestDirected(t *testing.T) {
for _, traverse := range tc.traverseTests {
t.Run(traverse.name, func(t *testing.T) {
visited := make([]int, 0)
g.Traverse(traverse.start, traverse.strategy, traverse.order, func(v int) {
visited = append(visited, v)
g.Traverse(traverse.start, traverse.strategy, traverse.order, &Visitor{
VisitVertex: func(v int) {
visited = append(visited, v)
},
})
assert.Equal(t, traverse.expectedVisits, visited)
})
Expand Down
13 changes: 9 additions & 4 deletions ds/graph/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ const (
PostOrder
)

type (
// The VisitFunc type is a function for visiting a graph vertex.
VisitFunc func(int)
)
// Visitor provides methods for visiting vertices and edges when traversing a graph.
// VisitVertex is called when visiting a vertex in a graph (undirected, directed, weighted-undirected, and weighted-directed).
// VisitEdge is called when visiting an edge in a graph (undirected and directed).
// VisitWeightedEdge is called when visiting an edge in a graph (weighted-undirected and weighted-directed).
type Visitor struct {
VisitVertex func(int)
VisitEdge func(int, int)
VisitWeightedEdge func(int, int, float64)
}
Loading

0 comments on commit 97209c2

Please sign in to comment.