Go的数据结构与实现【Graph】

介绍

图是网络结构的表示。现实世界中有大量图谱示例,互联网和社交图谱就是经典示例。图基本上是一组由边连接的节点。

实现

实现思路

图形数据结构将实现这些方法:

AddNode():添加一个节点到图里

AddEdge():添加一条边到图里

Print():打印图结构

图结构定义为:

go 复制代码
type Graph struct {
   sync.RWMutex
   nodes []*Node
   edges map[Node][]*Node
}

节点定义为:

go 复制代码
import "fmt"

type T string

type Node struct {
   value T
}

// Print a node
func (n *Node) Print() string {
   return fmt.Sprintf("%v", n.value)
}

这里将实现一个无向图,这意味着从A到B添加一条边也会从B到A添加一条边。

go 复制代码
func NewGraph() *Graph {
   g := &Graph{
      nodes: []*Node{},
      edges: make(map[Node][]*Node),
   }

   return g
}

// AddNode adds a node to the graph
func (g *Graph) AddNode(n *Node) {
   g.Lock()
   g.nodes = append(g.nodes, n)
   g.Unlock()
}

// AddEdge adds an edge to the graph
func (g *Graph) AddEdge(n1, n2 *Node) {
   g.Lock()
   defer g.Unlock()

   if g.edges == nil {
      g.edges = make(map[Node][]*Node)
   }
   g.edges[*n1] = append(g.edges[*n1], n2)
   g.edges[*n2] = append(g.edges[*n2], n1)
}

// Print whole graph
func (g *Graph) Print() {
   g.Lock()
   defer g.Unlock()

   ret := ""
   for i := 0; i < len(g.nodes); i++ {
      ret += g.nodes[i].Print() + " -> "
      neighborhood := g.edges[*g.nodes[i]]
      for j := 0; j < len(neighborhood); j++ {
         ret += neighborhood[j].Print() + " "
      }
      ret += "\n"
   }

   fmt.Println(ret)
}

单元测试

这是一个测试,运行时将填充图结构并打印:

go 复制代码
import "testing"

var (
   nA = &Node{"A"}
   nB = &Node{"B"}
   nC = &Node{"C"}
   nD = &Node{"D"}
   nE = &Node{"E"}
   nF = &Node{"F"}
)

func InitGraph() *Graph {
   g := NewGraph()
   g.AddNode(nA)
   g.AddNode(nB)
   g.AddNode(nC)
   g.AddNode(nD)
   g.AddNode(nE)
   g.AddNode(nF)

   g.AddEdge(nA, nB)
   g.AddEdge(nA, nC)
   g.AddEdge(nB, nE)
   g.AddEdge(nC, nE)
   g.AddEdge(nE, nF)
   g.AddEdge(nD, nA)

   return g
}

func TestGraph_Print(t *testing.T) {
   g := InitGraph()
   g.Print()
}

输出:

go 复制代码
A -> B C D 
B -> A E 
C -> A E 
D -> A 
E -> B C F 
F -> E 

--- PASS: TestGraph_Print (0.00s)
PASS

遍历

BFS(广度优先搜索)是最广为人知的遍历图的算法之一。从一个节点开始,它首先遍历其所有直接链接的节点,然后处理链接到这些节点的节点,依此类推。

它是使用队列实现的,我们可以用之前实现的队列数据结构来辅助完成这个算法:

go 复制代码
// Traverse implements the BFS traversing algorithm
func (g *Graph) Traverse(f func(*Node)) {
   g.RLock()
   q := NewNodeQueue()
   n := g.nodes[0]
   q.Enqueue(n)
   visited := make(map[*Node]bool)
   for {
      if q.IsEmpty() {
         break
      }
      node, _ := q.Dequeue()
      visited[node] = true
      near := g.edges[*node]

      for i := 0; i < len(near); i++ {
         j := near[i]
         if !visited[j] {
            q.Enqueue(j)
            visited[j] = true
         }
      }
      if f != nil {
         f(node)
      }
   }
   g.RUnlock()
}

我们对算法进行测试:

go 复制代码
func TestGraph_Traverse(t *testing.T) {
   g := InitGraph()
   g.Traverse(func(n *Node) {
      fmt.Printf("%v\n", n.value)
   })
}

输出结果:

go 复制代码
A
B
C
D
E
F
--- PASS: TestGraph_Traverse (0.00s)
PASS
相关推荐
昂子的博客2 分钟前
基础数据结构——队列(链表实现)
数据结构
WaaTong3 分钟前
Java反射
java·开发语言·反射
Troc_wangpeng4 分钟前
R language 关于二维平面直角坐标系的制作
开发语言·机器学习
努力的家伙是不讨厌的6 分钟前
解析json导出csv或者直接入库
开发语言·python·json
Envyᥫᩣ20 分钟前
C#语言:从入门到精通
开发语言·c#
童先生41 分钟前
Go 项目中实现类似 Java Shiro 的权限控制中间件?
开发语言·go
lulu_gh_yu42 分钟前
数据结构之排序补充
c语言·开发语言·数据结构·c++·学习·算法·排序算法
Re.不晚1 小时前
Java入门15——抽象类
java·开发语言·学习·算法·intellij-idea
老秦包你会1 小时前
Qt第三课 ----------容器类控件
开发语言·qt
凤枭香1 小时前
Python OpenCV 傅里叶变换
开发语言·图像处理·python·opencv