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
相关推荐
海威的技术博客4 分钟前
JS中的原型与原型链
开发语言·javascript·原型模式
WPG大大通12 分钟前
基于DIODES AP43781+PI3USB31531+PI3DPX1207C的USB-C PD& Video 之全功能显示器连接端口方案
c语言·开发语言·计算机外设·开发板·电源·大大通
从以前26 分钟前
【算法题解】Bindian 山丘信号问题(E. Bindian Signaling)
开发语言·python·算法
high20111 小时前
【Java 基础】-- ArrayList 和 Linkedlist
java·开发语言
1nullptr1 小时前
lua和C API库一些记录
开发语言·lua
Jerry Nan1 小时前
Lua元表
开发语言·lua
?333331 小时前
CTFHub Web进阶-PHP-Bypass disable_function攻略
开发语言·安全·web安全·php
所以经济危机就是没有新技术拉动增长了1 小时前
二、javascript的进阶知识
开发语言·javascript·ecmascript
Bubluu1 小时前
浏览器点击视频裁剪当前帧,然后粘贴到页面
开发语言·javascript·音视频