243 lines
5.3 KiB
Go
243 lines
5.3 KiB
Go
package dijkstra
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"sync"
|
|
)
|
|
|
|
type ItemGraph struct {
|
|
Nodes []*Node
|
|
Edges map[Node][]*Edge
|
|
lock sync.RWMutex
|
|
}
|
|
|
|
// AddNode adds a node to the graph
|
|
func (g *ItemGraph) AddNode(n *Node) {
|
|
g.lock.Lock()
|
|
g.Nodes = append(g.Nodes, n)
|
|
g.lock.Unlock()
|
|
}
|
|
|
|
// AddEdge adds an edge to the graph
|
|
func (g *ItemGraph) AddEdge(n1, n2 *Node, weight int) {
|
|
g.lock.Lock()
|
|
if g.Edges == nil {
|
|
g.Edges = make(map[Node][]*Edge)
|
|
}
|
|
ed1 := Edge{
|
|
Node: n2,
|
|
Weight: weight,
|
|
}
|
|
|
|
ed2 := Edge{
|
|
Node: n1,
|
|
Weight: weight,
|
|
}
|
|
g.Edges[*n1] = append(g.Edges[*n1], &ed1)
|
|
g.Edges[*n2] = append(g.Edges[*n2], &ed2)
|
|
g.lock.Unlock()
|
|
}
|
|
|
|
// dijkstra implement
|
|
func getShortestPath(startNode *Node, endNode *Node, g *ItemGraph) ([]Point, int) {
|
|
visited := make(map[Point]bool)
|
|
dist := make(map[Point]int)
|
|
prev := make(map[Point]Point)
|
|
//pq := make(PriorityQueue, 1)
|
|
//heap.Init(&pq)
|
|
q := NodeQueue{}
|
|
pq := q.NewQ()
|
|
start := Vertex{
|
|
Node: startNode,
|
|
Distance: 0,
|
|
}
|
|
for _, nval := range g.Nodes {
|
|
dist[nval.Value] = math.MaxInt64
|
|
}
|
|
dist[startNode.Value] = start.Distance
|
|
pq.Enqueue(start)
|
|
//im := 0
|
|
for !pq.IsEmpty() {
|
|
v := pq.Dequeue()
|
|
if visited[v.Node.Value] {
|
|
continue
|
|
}
|
|
visited[v.Node.Value] = true
|
|
near := g.Edges[*v.Node]
|
|
|
|
for _, val := range near {
|
|
if !visited[val.Node.Value] {
|
|
if dist[v.Node.Value]+val.Weight < dist[val.Node.Value] {
|
|
store := Vertex{
|
|
Node: val.Node,
|
|
Distance: dist[v.Node.Value] + val.Weight,
|
|
}
|
|
dist[val.Node.Value] = dist[v.Node.Value] + val.Weight
|
|
//prev[val.Node.Value] = fmt.Sprintf("->%s", v.Node.Value)
|
|
prev[val.Node.Value] = v.Node.Value
|
|
pq.Enqueue(store)
|
|
}
|
|
//visited[val.Node.value] = true
|
|
}
|
|
}
|
|
}
|
|
// fmt.Println(dist)
|
|
// fmt.Println(prev)
|
|
pathval := prev[endNode.Value]
|
|
var finalArr []Point
|
|
finalArr = append(finalArr, endNode.Value)
|
|
for pathval != startNode.Value {
|
|
finalArr = append(finalArr, pathval)
|
|
pathval = prev[pathval]
|
|
}
|
|
finalArr = append(finalArr, pathval)
|
|
fmt.Println(finalArr)
|
|
for i, j := 0, len(finalArr)-1; i < j; i, j = i+1, j-1 {
|
|
finalArr[i], finalArr[j] = finalArr[j], finalArr[i]
|
|
}
|
|
return finalArr, dist[endNode.Value]
|
|
|
|
}
|
|
|
|
func getAllShortestPaths(startNode *Node, endNode *Node, g *ItemGraph) ([][]Point, int) {
|
|
visited := make(map[Point]bool)
|
|
dist := make(map[Point]int)
|
|
prev := make(map[Point][]Point) // Store multiple predecessors for each node
|
|
q := NodeQueue{}
|
|
pq := q.NewQ()
|
|
|
|
start := Vertex{
|
|
Node: startNode,
|
|
Distance: 0,
|
|
}
|
|
|
|
for _, n := range g.Nodes {
|
|
dist[n.Value] = math.MaxInt64
|
|
}
|
|
dist[startNode.Value] = start.Distance
|
|
pq.Enqueue(start)
|
|
|
|
// Perform Dijkstra's algorithm
|
|
for !pq.IsEmpty() {
|
|
v := pq.Dequeue()
|
|
if visited[v.Node.Value] {
|
|
continue
|
|
}
|
|
visited[v.Node.Value] = true
|
|
near := g.Edges[*v.Node]
|
|
|
|
for _, edge := range near {
|
|
alt := dist[v.Node.Value] + edge.Weight
|
|
|
|
// Case 1: Found a shorter path
|
|
if alt < dist[edge.Node.Value] {
|
|
dist[edge.Node.Value] = alt
|
|
prev[edge.Node.Value] = []Point{v.Node.Value} // Reset predecessors
|
|
pq.Enqueue(Vertex{
|
|
Node: edge.Node,
|
|
Distance: alt,
|
|
})
|
|
}
|
|
|
|
// Case 2: Found an equally short path
|
|
if alt == dist[edge.Node.Value] {
|
|
// Add the current node as a valid predecessor if not already present
|
|
found := false
|
|
for _, p := range prev[edge.Node.Value] {
|
|
if p == v.Node.Value {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
prev[edge.Node.Value] = append(prev[edge.Node.Value], v.Node.Value)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Iteratively reconstruct all paths
|
|
var paths [][]Point
|
|
stack := []struct {
|
|
node Point
|
|
path []Point
|
|
}{
|
|
{node: endNode.Value, path: []Point{}},
|
|
}
|
|
|
|
for len(stack) > 0 {
|
|
// Pop the top item from the stack
|
|
current := stack[len(stack)-1]
|
|
stack = stack[:len(stack)-1]
|
|
|
|
// Prepend the current node to the path
|
|
newPath := append([]Point{current.node}, current.path...)
|
|
|
|
// If we've reached the start node, save the path
|
|
if current.node.X == startNode.Value.X && current.node.Y == startNode.Value.Y {
|
|
paths = append(paths, newPath)
|
|
continue
|
|
}
|
|
|
|
// Push all predecessors onto the stack
|
|
for _, predecessor := range prev[current.node] {
|
|
if !Contains(newPath, predecessor) {
|
|
stack = append(stack, struct {
|
|
node Point
|
|
path []Point
|
|
}{
|
|
node: predecessor,
|
|
path: newPath,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
return paths, dist[endNode.Value]
|
|
}
|
|
|
|
func Contains(slice []Point, value Point) bool {
|
|
for _, v := range slice {
|
|
if v == value { // Compare values
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func CreateGraph(data InputGraph) *ItemGraph {
|
|
var g ItemGraph
|
|
nodes := make(map[Point]*Node)
|
|
for _, v := range data.Graph {
|
|
if _, found := nodes[v.Source]; !found {
|
|
nA := Node{v.Source}
|
|
nodes[v.Source] = &nA
|
|
g.AddNode(&nA)
|
|
}
|
|
if _, found := nodes[v.Destination]; !found {
|
|
nA := Node{v.Destination}
|
|
nodes[v.Destination] = &nA
|
|
g.AddNode(&nA)
|
|
}
|
|
g.AddEdge(nodes[v.Source], nodes[v.Destination], v.Weight)
|
|
}
|
|
return &g
|
|
}
|
|
|
|
func GetShortestPath(from, to Point, g *ItemGraph) ([]Point, int) {
|
|
nA := &Node{from}
|
|
nB := &Node{to}
|
|
|
|
path, distance := getShortestPath(nA, nB, g)
|
|
return path, distance
|
|
}
|
|
|
|
func GetAllShortestPaths(from, to Point, g *ItemGraph) ([][]Point, int) {
|
|
nA := &Node{from}
|
|
nB := &Node{to}
|
|
|
|
paths, distance := getAllShortestPaths(nA, nB, g)
|
|
return paths, distance
|
|
}
|