github.com/qiuhoude/go-web@v0.0.0-20220223060959-ab545e78f20d/algorithm/datastructures/graph/dijkstra.go (about) 1 package graph 2 3 import ( 4 "fmt" 5 "github.com/qiuhoude/go-web/algorithm/datastructures/queue" 6 "github.com/qiuhoude/go-web/algorithm/datastructures/stack" 7 ) 8 9 type vertex struct { 10 id int 11 v interface{} 12 } 13 14 func (v *vertex) String() string { 15 return fmt.Sprintf("(%v, %v)", v.id, v.v) 16 } 17 18 func newVertex(id int, v interface{}) *vertex { 19 return &vertex{ 20 id: id, 21 v: v, 22 } 23 } 24 25 type edge struct { 26 sid int // 起点id 27 tid int // 目标id 28 weight int // 权重 29 } 30 31 func newEdge(sid, tid, weight int) *edge { 32 return &edge{ 33 sid: sid, 34 tid: tid, 35 weight: weight, 36 } 37 } 38 39 // 距离起点的距离 40 type vertexDis struct { 41 id int 42 dis int // 距离起点的距离 43 } 44 45 func (v *vertexDis) String() string { 46 return fmt.Sprintf("(%v, %v)", v.id, v.dis) 47 } 48 49 func newVertexDis(id, dis int) *vertexDis { 50 return &vertexDis{id: id, dis: dis} 51 } 52 53 // 有向图 54 type Digraph struct { 55 adj map[int]*vertex 56 edges map[int][]*edge // [顶点起点id] 57 } 58 59 func NewGraph() *Digraph { 60 return &Digraph{ 61 adj: make(map[int]*vertex), 62 edges: make(map[int][]*edge), 63 } 64 } 65 66 func (g *Digraph) ContainsVertex(id int) bool { 67 _, ok := g.adj[id] 68 return ok 69 } 70 71 func (g *Digraph) ContainsEdge(sid, tid int) bool { 72 if eds, ok := g.edges[sid]; ok { 73 for i := range eds { 74 if tid == eds[i].tid { 75 return true 76 } 77 } 78 } 79 return false 80 } 81 82 func (g *Digraph) AddVertex(id int, v interface{}) { 83 if !g.ContainsVertex(id) { 84 g.adj[id] = newVertex(id, v) 85 } 86 } 87 88 // 添加单向边 89 func (g *Digraph) AddEdge(sid, tid, weight int) { 90 if g.ContainsVertex(sid) && g.ContainsVertex(tid) && sid != tid { 91 if !g.ContainsEdge(sid, tid) { 92 eds := g.edges[sid] 93 eds = append(eds, newEdge(sid, tid, weight)) 94 g.edges[sid] = eds 95 } 96 } 97 } 98 99 func (g *Digraph) GetVertexData(id int) interface{} { 100 if ret, ok := g.adj[id]; ok { 101 return ret.v 102 } 103 return nil 104 } 105 106 // 添加双向边 107 func (g *Digraph) AddDuplexEdge(sid, tid, weight int) { 108 g.AddEdge(sid, tid, weight) 109 g.AddEdge(tid, sid, weight) 110 } 111 112 /* 113 最短路径算法还有 Bellford 算法、Floyd 算法等 114 115 Dijkstra 最短路径算法 搜索 时间 O(E*logV) logV是优先队列的是复杂度 116 */ 117 func (g *Digraph) DijkstraSearch(sid, tid int) []int { 118 if sid == tid { // 同一个点 119 return nil 120 } 121 // 两个点必须存在 122 if !g.ContainsVertex(sid) || !g.ContainsVertex(tid) { 123 return nil 124 } 125 vertexDisMap := make(map[int]*vertexDis) 126 for id := range g.adj { 127 vertexDisMap[id] = newVertexDis(id, 0x7fffffff) //初始值 最大距离 128 } 129 130 n := len(g.adj) 131 prev := make(map[int]int, n) // precurosor记录前驱节点,用于反向找path 132 visited := make(map[int]bool, n) // 访问标记 133 visited[sid] = true 134 que := queue.NewPriorityQueue(compareEdge) 135 vertexDisMap[sid].dis = 0 // 起点到起点距离是0 136 que.Enqueue(vertexDisMap[sid]) 137 isFound := false 138 139 for !que.IsEmpty() { 140 minVertex := que.Dequeue().(*vertexDis) 141 if minVertex.id == tid { 142 isFound = true 143 break 144 } 145 // 取出一条 minVertex 相连的边 146 for _, edge := range g.edges[minVertex.id] { 147 // 找到一条到 nextVertex 更短的路径 148 nextVertex := vertexDisMap[edge.tid] // minVertex-->nextVertex 149 if nextVertex.dis > minVertex.dis+edge.weight { 150 nextVertex.dis = minVertex.dis + edge.weight 151 prev[nextVertex.id] = minVertex.id 152 if !visited[nextVertex.id] { 153 visited[nextVertex.id] = true 154 que.Enqueue(vertexDisMap[nextVertex.id]) 155 } 156 } 157 } 158 } 159 fmt.Println(vertexDisMap) 160 if isFound { 161 cur := tid 162 st := stack.NewArrayStack() 163 164 for i := 0; i < len(prev); i++ { //反向查找,就是路径 165 if _, ok := prev[cur]; !ok { 166 break 167 } 168 st.Push(cur) 169 cur = prev[cur] 170 } 171 st.Push(sid) 172 173 ret := make([]int, st.Len()) 174 175 for i := 0; !st.IsEmpty(); i++ { 176 ret[i] = st.Pop().(int) 177 } 178 return ret 179 } 180 return nil 181 } 182 183 // 边的比较 184 func compareEdge(v1, v2 interface{}) int { 185 ed1 := v1.(*vertexDis) 186 ed2 := v2.(*vertexDis) 187 if ed1.dis > ed2.dis { 188 return 1 189 } else if ed1.dis < ed2.dis { 190 return -1 191 } else { 192 return 0 193 } 194 }