Files
adventofcode/2025/go/utils/dijkstra/graph.go
2025-12-01 14:04:11 +00:00

242 lines
5.3 KiB
Go

package dijkstra
import (
"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
}