Skip to content

Commit a33ce27

Browse files
author
computer0101
committed
implement a star algorithm for undirected graph
1 parent 8bb7a54 commit a33ce27

File tree

3 files changed

+352
-0
lines changed

3 files changed

+352
-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: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
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+
30+
if g[toVtx] == nil { // check if destination vertex exists
31+
fmt.Println("\n-- Destination vertex " + toVtx + " does not exist. --")
32+
return
33+
}
34+
35+
for i := range g[fromVtx] { // check if edge already exists
36+
if g[fromVtx][i].name == toVtx {
37+
fmt.Println("\n-- Edge between " + fromVtx + " and " + toVtx + " already exists. --")
38+
return
39+
}
40+
}
41+
42+
g[fromVtx] = append(g[fromVtx], Node{name: toVtx, value: edgeValue})
43+
g[toVtx] = append(g[toVtx], Node{name: fromVtx, value: edgeValue})
44+
return
45+
}
46+
47+
func (g graph) removeVertexFromGraph(vtx string) {
48+
length := len(g[vtx]) - 1
49+
for length != -1 {
50+
g.removeEdgeFromGraph(vtx, g[vtx][length].name)
51+
length--
52+
}
53+
delete(g, vtx)
54+
delete(vtxWeights, vtx)
55+
}
56+
57+
func (g graph) removeEdgeFromGraph(fromVtx, toVtx string) {
58+
if g[fromVtx] == nil || g[toVtx] == nil {
59+
fmt.Println("\n-- Edge between " + fromVtx + " and " + toVtx + " does not exist. --")
60+
return
61+
}
62+
63+
for i := range g[fromVtx] {
64+
if g[fromVtx][i].name == toVtx {
65+
if i == 0 {
66+
g[fromVtx] = g[fromVtx][1:len(g[fromVtx])]
67+
} else if i == (len(g[fromVtx]) - 1) {
68+
g[fromVtx] = g[fromVtx][0:(len(g[fromVtx]) - 1)]
69+
} else {
70+
initial := g[fromVtx][0:i]
71+
final := g[fromVtx][i+1 : len(g[fromVtx])]
72+
g[fromVtx] = append(initial, final...)
73+
}
74+
break
75+
}
76+
}
77+
78+
for i := range g[toVtx] {
79+
if g[toVtx][i].name == fromVtx {
80+
if i == 0 {
81+
g[toVtx] = g[toVtx][1:len(g[toVtx])]
82+
} else if i == (len(g[toVtx]) - 1) {
83+
g[toVtx] = g[toVtx][0:(len(g[toVtx]) - 1)]
84+
} else {
85+
initial := g[toVtx][0:i]
86+
final := g[toVtx][i+1 : len(g[toVtx])]
87+
g[toVtx] = append(initial, final...)
88+
}
89+
break
90+
}
91+
}
92+
}
93+
94+
func main() {
95+
i := 0
96+
g := make(graph)
97+
vtxWeights = make(map[string]int)
98+
for i == 0 {
99+
fmt.Println("\n1. ADD A VERTEX")
100+
fmt.Println("2. ADD AN EDGE")
101+
fmt.Println("3. REMOVE VERTEX")
102+
fmt.Println("4. REMOVE AN EDGE")
103+
fmt.Println("5. RUN A STAR ALGO")
104+
fmt.Println("6. DISPLAY GRAPH")
105+
fmt.Println("7. EXIT")
106+
var choice int
107+
fmt.Print("Enter your choice: ")
108+
fmt.Scanf("%d", &choice)
109+
switch choice {
110+
case 1:
111+
g.addVertex()
112+
case 2:
113+
g.addEdge()
114+
case 3:
115+
g.removeVertex()
116+
case 4:
117+
g.removeEdge()
118+
case 5:
119+
g.initAStarAlgo()
120+
case 6:
121+
g.simpleDisplay()
122+
case 7:
123+
i = 1
124+
default:
125+
fmt.Println("Command not recognized.")
126+
}
127+
}
128+
}
129+
130+
func (g graph) addVertex() {
131+
var vtxName string
132+
var vtxWeight int
133+
fmt.Print("Enter the name of vertex: ")
134+
fmt.Scanf("%s", &vtxName)
135+
fmt.Print("Enter the weight of vertex: ")
136+
fmt.Scanf("%d", &vtxWeight)
137+
g.addVertexToGraph(vtxName, vtxWeight)
138+
}
139+
140+
func (g graph) addEdge() {
141+
var fromVtx, toVtx string
142+
var edgeValue int
143+
fmt.Print("Enter the initial vertex name: ")
144+
fmt.Scanf("%s", &fromVtx)
145+
fmt.Print("Enter the destination vertex name: ")
146+
fmt.Scanf("%s", &toVtx)
147+
fmt.Print("Enter the weight of edge: ")
148+
fmt.Scanf("%d", &edgeValue)
149+
g.addEdgeToGraph(fromVtx, toVtx, edgeValue)
150+
}
151+
152+
func (g graph) removeVertex() {
153+
var vtxName string
154+
fmt.Print("Enter the name of vertex: ")
155+
fmt.Scanf("%s", &vtxName)
156+
g.removeVertexFromGraph(vtxName)
157+
}
158+
159+
func (g graph) removeEdge() {
160+
var fromVtx, toVtx string
161+
fmt.Print("Enter the initial vertex name: ")
162+
fmt.Scanf("%s", &fromVtx)
163+
fmt.Print("Enter the destination vertex name: ")
164+
fmt.Scanf("%s", &toVtx)
165+
g.removeEdgeFromGraph(fromVtx, toVtx)
166+
}
167+
168+
func (g graph) initAStarAlgo() {
169+
var initialVtx, goalVtx string
170+
fmt.Print("Enter the initial vertex name: ")
171+
fmt.Scanf("%s", &initialVtx)
172+
fmt.Print("Enter the goal vertex name: ")
173+
fmt.Scanf("%s", &goalVtx)
174+
if g[initialVtx] == nil {
175+
fmt.Println("Initial vertex does not exist.")
176+
return
177+
} else if g[goalVtx] == nil {
178+
fmt.Println("Initial vertex does not exist.")
179+
return
180+
}
181+
g.runAStarAlgo(initialVtx, goalVtx)
182+
}
183+
184+
func (g graph) simpleDisplay() {
185+
fmt.Println("")
186+
for i := range g {
187+
fmt.Print(i, " => ")
188+
for j := range g[i] {
189+
fmt.Print(g[i][j])
190+
}
191+
fmt.Println("")
192+
}
193+
}
194+
195+
// g.addVertexToGraph("a", 10)
196+
// g.addVertexToGraph("b", 8)
197+
// g.addVertexToGraph("c", 5)
198+
// g.addVertexToGraph("d", 7)
199+
// g.addVertexToGraph("e", 3)
200+
// g.addVertexToGraph("f", 6)
201+
// g.addVertexToGraph("g", 5)
202+
// g.addVertexToGraph("h", 3)
203+
// g.addVertexToGraph("i", 1)
204+
// g.addVertexToGraph("j", 0)
205+
// g.addEdgeToGraph("a", "b", 6)
206+
// g.addEdgeToGraph("a", "f", 3)
207+
// g.addEdgeToGraph("b", "c", 3)
208+
// g.addEdgeToGraph("b", "d", 2)
209+
// g.addEdgeToGraph("c", "d", 1)
210+
// g.addEdgeToGraph("c", "e", 5)
211+
// g.addEdgeToGraph("d", "e", 8)
212+
// g.addEdgeToGraph("e", "i", 5)
213+
// g.addEdgeToGraph("e", "j", 5)
214+
// g.addEdgeToGraph("f", "g", 1)
215+
// g.addEdgeToGraph("f", "h", 7)
216+
// g.addEdgeToGraph("g", "i", 3)
217+
// g.addEdgeToGraph("h", "i", 2)
218+
// g.addEdgeToGraph("i", "j", 3)
219+
// g.runAStarAlgo("a", "j")
220+
// a-f-g-i-j 10
221+
222+
// g.addVertexToGraph("a", 14)
223+
// g.addVertexToGraph("b", 12)
224+
// g.addVertexToGraph("c", 11)
225+
// g.addVertexToGraph("d", 6)
226+
// g.addVertexToGraph("e", 4)
227+
// g.addVertexToGraph("f", 11)
228+
// g.addVertexToGraph("z", 0)
229+
// g.addEdgeToGraph("a", "b", 4)
230+
// g.addEdgeToGraph("a", "c", 3)
231+
// g.addEdgeToGraph("b", "e", 12)
232+
// g.addEdgeToGraph("b", "f", 5)
233+
// g.addEdgeToGraph("c", "d", 7)
234+
// g.addEdgeToGraph("c", "e", 10)
235+
// g.addEdgeToGraph("d", "e", 2)
236+
// g.addEdgeToGraph("e", "z", 5)
237+
// g.addEdgeToGraph("f", "z", 16)
238+
// g.runAStarAlgo("a", "z")
239+
// 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)