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  }