Skip to content

Commit c461e3a

Browse files
author
computer0101
committed
implement a star algorithm for directed graph
1 parent d6d68d4 commit c461e3a

File tree

3 files changed

+351
-0
lines changed

3 files changed

+351
-0
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
func (g graph) runAStarAlgo(initialVtx, goalVtx string) {
9+
for vtx := range g[initialVtx] {
10+
f := g[initialVtx][vtx].value + vtxWeights[g[initialVtx][vtx].name]
11+
Enqueue(&Node{name: initialVtx + "-" + g[initialVtx][vtx].name, value: f})
12+
}
13+
path := g.traverseFurther(Dequeue(), goalVtx)
14+
fmt.Println("Best Path is:", path.name)
15+
fmt.Println("The cost is:", path.value)
16+
}
17+
18+
func (g graph) traverseFurther(bestPath *Node, goalVtx string) *Node {
19+
vertices := strings.Split(bestPath.name, "-")
20+
currVtx := vertices[len(vertices)-1]
21+
for vtx := range g[currVtx] {
22+
visitingVtx := g[currVtx][vtx]
23+
f := bestPath.value - vtxWeights[currVtx] + g[currVtx][vtx].value + vtxWeights[visitingVtx.name]
24+
Enqueue(&Node{name: bestPath.name + "-" + visitingVtx.name, value: f})
25+
}
26+
27+
nextBestPath := Dequeue()
28+
vertices = strings.Split(nextBestPath.name, "-")
29+
if vertices[len(vertices)-1] != goalVtx {
30+
return g.traverseFurther(nextBestPath, goalVtx)
31+
}
32+
33+
return nextBestPath
34+
}
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
package main
2+
3+
import "fmt"
4+
5+
// Node is a single node in a graph list
6+
type Node struct {
7+
name string
8+
value int
9+
}
10+
11+
type graph map[string][]Node
12+
13+
var vtxWeights map[string]int
14+
15+
func (g graph) addVertexToGraph(vtx string, vtxWeight int) {
16+
if g[vtx] != nil {
17+
fmt.Println("\n-- Vertex already exists. --")
18+
return
19+
}
20+
vtxWeights[vtx] = vtxWeight
21+
g[vtx] = make([]Node, 0)
22+
}
23+
24+
func (g graph) addEdgeToGraph(fromVtx, toVtx string, edgeValue int) {
25+
if g[fromVtx] == nil { // check if initial vertex exists
26+
fmt.Println("\n-- Initial vertex " + fromVtx + " does not exist. --")
27+
return
28+
}
29+
for i := range g[fromVtx] { // check if edge already exists
30+
if g[fromVtx][i].name == toVtx {
31+
fmt.Println("\n-- Edge from " + fromVtx + " to " + toVtx + " already exists. --")
32+
return
33+
}
34+
}
35+
if g[toVtx] == nil { // create new destination vertext if it does not exists
36+
g[toVtx] = make([]Node, 0)
37+
fmt.Println("\n-- Destination vertex " + toVtx + " created. --")
38+
}
39+
40+
g[fromVtx] = append(g[fromVtx], Node{name: toVtx, value: edgeValue})
41+
return
42+
}
43+
44+
func (g graph) removeVertexFromGraph(vtx string) {
45+
length := len(g[vtx]) - 1
46+
for length != -1 { // loop in reverse in order to avoid index out of range
47+
g.removeEdgeFromGraph(vtx, g[vtx][length].name)
48+
length--
49+
}
50+
51+
for i := range g {
52+
if i != vtx {
53+
length := len(g[i]) - 1
54+
for length != -1 { // loop in reverse in order to avoid index out of range
55+
if g[i][length].name == vtx {
56+
g.removeEdgeFromGraph(i, vtx)
57+
}
58+
length--
59+
}
60+
}
61+
}
62+
63+
delete(g, vtx)
64+
delete(g, vtx)
65+
}
66+
67+
func (g graph) removeEdgeFromGraph(fromVtx, toVtx string) {
68+
if g[fromVtx] == nil {
69+
fmt.Println("\n-- Vertex " + fromVtx + " does not exists. --")
70+
return
71+
} else if g[toVtx] == nil {
72+
fmt.Println("\n-- Vertex " + toVtx + " does not exists. --")
73+
return
74+
}
75+
76+
for i := range g[fromVtx] {
77+
if g[fromVtx][i].name == toVtx {
78+
if i == 0 {
79+
g[fromVtx] = g[fromVtx][1:len(g[fromVtx])]
80+
} else if i == (len(g[fromVtx]) - 1) {
81+
g[fromVtx] = g[fromVtx][0:(len(g[fromVtx]) - 1)]
82+
} else {
83+
initial := g[fromVtx][0:i]
84+
final := g[fromVtx][i+1 : len(g[fromVtx])]
85+
g[fromVtx] = append(initial, final...)
86+
}
87+
break
88+
}
89+
}
90+
}
91+
92+
func main() {
93+
i := 0
94+
g := make(graph)
95+
vtxWeights = make(map[string]int)
96+
97+
for i == 0 {
98+
fmt.Println("\n1. ADD A VERTEX")
99+
fmt.Println("2. ADD AN EDGE")
100+
fmt.Println("3. REMOVE VERTEX")
101+
fmt.Println("4. REMOVE AN EDGE")
102+
fmt.Println("5. RUN A STAR ALGO")
103+
fmt.Println("6. DISPLAY GRAPH")
104+
fmt.Println("7. EXIT")
105+
var choice int
106+
fmt.Print("Enter your choice: ")
107+
fmt.Scanf("%d", &choice)
108+
switch choice {
109+
case 1:
110+
g.addVertex()
111+
case 2:
112+
g.addEdge()
113+
case 3:
114+
g.removeVertex()
115+
case 4:
116+
g.removeEdge()
117+
case 5:
118+
g.initAStarAlgo()
119+
case 6:
120+
g.simpleDisplay()
121+
case 7:
122+
i = 1
123+
default:
124+
fmt.Println("Command not recognized.")
125+
}
126+
}
127+
}
128+
129+
func (g graph) addVertex() {
130+
var vtxName string
131+
var vtxWeight int
132+
fmt.Print("Enter the name of vertex: ")
133+
fmt.Scanf("%s", &vtxName)
134+
fmt.Print("Enter the weight of vertex: ")
135+
fmt.Scanf("%d", &vtxWeight)
136+
g.addVertexToGraph(vtxName, vtxWeight)
137+
}
138+
139+
func (g graph) addEdge() {
140+
var fromVtx, toVtx string
141+
var edgeValue int
142+
fmt.Print("Enter the initial vertex name: ")
143+
fmt.Scanf("%s", &fromVtx)
144+
fmt.Print("Enter the destination vertex name: ")
145+
fmt.Scanf("%s", &toVtx)
146+
fmt.Print("Enter the weight of edge: ")
147+
fmt.Scanf("%d", &edgeValue)
148+
g.addEdgeToGraph(fromVtx, toVtx, edgeValue)
149+
}
150+
151+
func (g graph) removeVertex() {
152+
var vtxName string
153+
fmt.Print("Enter the name of vertex: ")
154+
fmt.Scanf("%s", &vtxName)
155+
g.removeVertexFromGraph(vtxName)
156+
}
157+
158+
func (g graph) removeEdge() {
159+
var fromVtx, toVtx string
160+
fmt.Print("Enter the initial vertex name: ")
161+
fmt.Scanf("%s", &fromVtx)
162+
fmt.Print("Enter the destination vertex name: ")
163+
fmt.Scanf("%s", &toVtx)
164+
g.removeEdgeFromGraph(fromVtx, toVtx)
165+
}
166+
167+
func (g graph) initAStarAlgo() {
168+
var initialVtx, goalVtx string
169+
fmt.Print("Enter the initial vertex name: ")
170+
fmt.Scanf("%s", &initialVtx)
171+
fmt.Print("Enter the goal vertex name: ")
172+
fmt.Scanf("%s", &goalVtx)
173+
if g[initialVtx] == nil {
174+
fmt.Println("Initial vertex does not exist.")
175+
return
176+
} else if g[goalVtx] == nil {
177+
fmt.Println("Initial vertex does not exist.")
178+
return
179+
}
180+
g.runAStarAlgo(initialVtx, goalVtx)
181+
}
182+
183+
func (g graph) simpleDisplay() {
184+
fmt.Println("")
185+
for i := range g {
186+
fmt.Print(i, " => ")
187+
for j := range g[i] {
188+
fmt.Print(g[i][j])
189+
}
190+
fmt.Println("")
191+
}
192+
}
193+
194+
// g.addVertexToGraph("a", 10)
195+
// g.addVertexToGraph("b", 8)
196+
// g.addVertexToGraph("c", 5)
197+
// g.addVertexToGraph("d", 7)
198+
// g.addVertexToGraph("e", 3)
199+
// g.addVertexToGraph("f", 6)
200+
// g.addVertexToGraph("g", 5)
201+
// g.addVertexToGraph("h", 3)
202+
// g.addVertexToGraph("i", 1)
203+
// g.addVertexToGraph("j", 0)
204+
// g.addEdgeToGraph("a", "b", 6)
205+
// g.addEdgeToGraph("a", "f", 3)
206+
// g.addEdgeToGraph("b", "c", 3)
207+
// g.addEdgeToGraph("b", "d", 2)
208+
// g.addEdgeToGraph("c", "d", 1)
209+
// g.addEdgeToGraph("c", "e", 5)
210+
// g.addEdgeToGraph("d", "e", 8)
211+
// g.addEdgeToGraph("e", "i", 5)
212+
// g.addEdgeToGraph("e", "j", 5)
213+
// g.addEdgeToGraph("f", "g", 1)
214+
// g.addEdgeToGraph("f", "h", 7)
215+
// g.addEdgeToGraph("g", "i", 3)
216+
// g.addEdgeToGraph("h", "i", 2)
217+
// g.addEdgeToGraph("i", "j", 3)
218+
// g.runAStarAlgo("a", "j")
219+
// a-f-g-i-j 10
220+
221+
// g.addVertexToGraph("a", 14)
222+
// g.addVertexToGraph("b", 12)
223+
// g.addVertexToGraph("c", 11)
224+
// g.addVertexToGraph("d", 6)
225+
// g.addVertexToGraph("e", 4)
226+
// g.addVertexToGraph("f", 11)
227+
// g.addVertexToGraph("z", 0)
228+
// g.addEdgeToGraph("a", "b", 4)
229+
// g.addEdgeToGraph("a", "c", 3)
230+
// g.addEdgeToGraph("b", "e", 12)
231+
// g.addEdgeToGraph("b", "f", 5)
232+
// g.addEdgeToGraph("c", "d", 7)
233+
// g.addEdgeToGraph("c", "e", 10)
234+
// g.addEdgeToGraph("d", "e", 2)
235+
// g.addEdgeToGraph("e", "z", 5)
236+
// g.addEdgeToGraph("f", "z", 16)
237+
// g.runAStarAlgo("a", "z")
238+
// a-c-d-e-z 17
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package main
2+
3+
// Note - Lower priority value processes will be run first. Hence this is implemented using min heap.
4+
5+
import (
6+
"fmt"
7+
"math"
8+
)
9+
10+
var heap []*Node
11+
12+
// Enqueue inserts a node in a heap
13+
func Enqueue(node *Node) {
14+
heap = append(heap, node)
15+
minHeapify()
16+
}
17+
18+
func minHeapify() {
19+
idx := len(heap) - 1
20+
element := heap[idx]
21+
for idx > 0 {
22+
parentIdx := int(math.Floor(float64((idx - 1) / 2)))
23+
parent := heap[parentIdx]
24+
if element.value >= parent.value {
25+
break
26+
}
27+
heap[parentIdx] = element
28+
heap[idx] = parent
29+
idx = parentIdx
30+
}
31+
}
32+
33+
// Dequeue will remove a node from heap
34+
func Dequeue() *Node {
35+
if len(heap) == 0 || heap[0] == nil {
36+
fmt.Println("\n-- Heap is empty. --")
37+
return nil
38+
}
39+
min := heap[0]
40+
end := heap[len(heap)-1]
41+
heap = heap[0 : len(heap)-1]
42+
if len(heap) > 0 {
43+
heap[0] = end
44+
bubbleDown()
45+
}
46+
return min
47+
}
48+
49+
func bubbleDown() {
50+
idx := 0
51+
length := len(heap)
52+
element := heap[0]
53+
for true {
54+
leftChildIdx := (2 * idx) + 1
55+
rightChildIdx := (2 * idx) + 2
56+
var leftChild, rightChild *Node
57+
var swap int
58+
59+
if leftChildIdx < length {
60+
leftChild = heap[leftChildIdx]
61+
if leftChild.value < element.value {
62+
swap = leftChildIdx
63+
}
64+
}
65+
if rightChildIdx < length {
66+
rightChild = heap[rightChildIdx]
67+
if (rightChild.value < element.value && swap == 0) || (rightChild.value < leftChild.value && swap != 0) {
68+
swap = rightChildIdx
69+
}
70+
}
71+
72+
if swap == 0 {
73+
break
74+
}
75+
heap[idx] = heap[swap]
76+
heap[swap] = element
77+
idx = swap
78+
}
79+
}

0 commit comments

Comments
 (0)