github.com/qiuhoude/go-web@v0.0.0-20220223060959-ab545e78f20d/algorithm/datastructures/graph/graph.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 /* 10 基本概念 11 方向维度: 无向图 有向图 12 权重维度: 带权图 非带权图 13 稀疏维度: 稠密图 稀疏图 14 15 图的存储方式 16 1. 邻接矩阵 17 2. 邻接表(adjacency table) , 优化后 可以用跳表 或 树结构代替普通链表加快查询 18 19 搜索算法 20 1. BST(Breadth-First-Search) 广度优先 借用queue 21 时间 O(V+E):V 表示顶点的个数,E 表示边的个数 22 空间 O(V) visited、prev 23 24 25 2. DST(Depth-First-Search) 深度优先 借用stack 26 时间 O(E) 每条边最多会被访问两次,一次是遍历,一次是回退 27 空间 O(V) 主要消耗在 visited、prev 数组和递归调用栈,但最大深度不会超过V个节点 28 */ 29 30 // 图的节点 31 type node struct { 32 id int // 节点id 33 degree map[int]*node // 连接的节点 34 v interface{} // 节点存储数据 35 } 36 37 func newGraphNode(id int, v interface{}) *node { 38 return &node{ 39 id: id, 40 degree: make(map[int]*node), 41 v: v, 42 } 43 } 44 45 // 邻接表的无向图 46 type GraphAdjTab struct { 47 adj map[int]*node 48 // 此处可以有个逆邻接表 49 } 50 51 func NewGraphAdjTab() *GraphAdjTab { 52 return &GraphAdjTab{adj: make(map[int]*node)} 53 } 54 55 // 向无向图中添加顶点 56 func (g *GraphAdjTab) AddVertex(id int, v interface{}) { 57 if c, ok := g.adj[id]; ok { 58 c.v = v 59 } else { 60 g.adj[id] = newGraphNode(id, v) 61 } 62 } 63 64 // 向无向图中添加边, 点必须存在 65 func (g *GraphAdjTab) AddEdge(sid, tid int) bool { 66 if !g.containVertex(sid) || !g.containVertex(tid) { 67 return false 68 } 69 70 sNode := g.adj[sid] 71 tNode := g.adj[tid] 72 sNode.degree[tid] = tNode 73 tNode.degree[sid] = sNode 74 return true 75 } 76 77 // 是否包含顶点 78 func (g *GraphAdjTab) containVertex(id int) bool { 79 _, ok := g.adj[id] 80 return ok 81 } 82 83 // 根据id获取节点数据 84 func (g *GraphAdjTab) GetVertexData(id int) interface{} { 85 if ret, ok := g.adj[id]; ok { 86 return ret.v 87 } 88 return nil 89 } 90 91 // 广度优先路径搜索, sid 到 tid的路径 返回路线id 92 func (g *GraphAdjTab) BSTSearch(sid, tid int) []int { 93 if sid == tid { // 同一个点 94 return nil 95 } 96 // 两个点必须存在 97 if !g.containVertex(sid) || !g.containVertex(tid) { 98 return nil 99 } 100 101 n := len(g.adj) 102 prev := make(map[int]int, n) 103 visited := make(map[int]bool, n) // 访问标记 104 visited[sid] = true // 起点已经访问 105 que := queue.NewArrayQueue(n) 106 que.Enqueue(sid) 107 isFound := false 108 out: 109 for que.Len() > 0 && !isFound { 110 e := que.Dequeue() 111 id := e.(int) 112 node := g.adj[id] 113 for idk := range node.degree { 114 if !visited[idk] { // 没有被访问 115 prev[idk] = id 116 //fmt.Printf("%v->%v ;", id, idk) 117 if idk == tid { // 已经找到 118 isFound = true 119 break out 120 } 121 visited[idk] = true // 标记已访问 122 que.Enqueue(idk) 123 } 124 } 125 //fmt.Println() 126 } 127 if isFound { 128 cur := tid 129 st := stack.NewArrayStack() 130 for i := 0; i < len(prev); i++ { //反向查找,就是路径 131 if _, ok := prev[cur]; !ok { 132 break 133 } 134 st.Push(cur) 135 cur = prev[cur] 136 } 137 st.Push(sid) 138 ret := make([]int, st.Len()) 139 for i := 0; !st.IsEmpty(); i++ { 140 ret[i] = st.Pop().(int) 141 } 142 return ret 143 } 144 return nil 145 } 146 147 // 深度优先, sid 到 tid的路径 返回路线id 148 func (g *GraphAdjTab) DSTSearch(sid, tid int) []int { 149 if sid == tid { // 同一个点 150 return nil 151 } 152 // 两个点必须存在 153 if !g.containVertex(sid) || !g.containVertex(tid) { 154 return nil 155 } 156 n := len(g.adj) 157 prev := make(map[int]int, n) 158 visited := make(map[int]bool, n) // 访问标记 159 visited[sid] = true // 起点已经访问 160 st := stack.NewArrayStack() 161 isFound := false 162 cur := g.adj[sid] 163 st.Push(sid) 164 out: 165 for !isFound && cur != nil { 166 flag := false // 是否还能继续往下找 167 for idk := range cur.degree { 168 if !visited[idk] { 169 st.Push(idk) 170 visited[idk] = true //标记以访问 171 prev[idk] = cur.id 172 fmt.Printf("forward %v->%v \n", cur.id, idk) 173 if idk == tid { // 已经找到 174 isFound = true 175 break out 176 } 177 cur = g.adj[idk] // 进行下一层 178 flag = true 179 break 180 } 181 } 182 if !flag && !st.IsEmpty() { 183 // 退栈 184 id := st.Pop().(int) 185 fmt.Printf("back %v->%v \n", cur.id, id) 186 cur = g.adj[id] 187 } 188 } 189 if isFound { 190 cur := tid 191 st := stack.NewArrayStack() 192 for i := 0; i < len(prev); i++ { //反向查找,就是路径 193 if _, ok := prev[cur]; !ok { 194 break 195 } 196 st.Push(cur) 197 cur = prev[cur] 198 } 199 st.Push(sid) 200 ret := make([]int, st.Len()) 201 for i := 0; !st.IsEmpty(); i++ { 202 ret[i] = st.Pop().(int) 203 } 204 return ret 205 } 206 return nil 207 }