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  }