golang数据结构_Go数据结构:图形

golang数据结构

A graph is a representation of a network structure. There are tons of graph real world examples, the Internet and the social graph being the classic ones.

是网络结构的表示。 有大量的图现实世界示例,互联网和社交图就是经典的例子。

It’s basically a set of nodes connected by edges.

它基本上是由edge连接的一组节点

I’ll skip the mathematical concepts since you can find them everywhere and jump directly to a Go implementation of a graph.

我将跳过数学概念,因为您可以在任何地方找到它们,然后直接跳转到图的Go实现。

实作 (Implementation)

A graph data structure will expose those methods:

图形数据结构将公开这些方法:

  • AddNode() inserts a node

    AddNode()插入一个节点

  • AddEdge() adds an edge between two nodes

    AddEdge()在两个节点之间添加一条边

and String() for inspection purposes.

String()用于检查目的。

A Graph is defined as

图定义为

type ItemGraph struct {
    nodes []*Node
    edges map[Node][]*Node
    lock  sync.RWMutex
}

with Node being

与节点是

type Node struct {
    value Item
}

I’ll implement an undirected graph, which means that adding an edge from A to B also adds an edge from B to A.

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

实作 (Implementation)

// Package graph creates a ItemGraph data structure for the Item type
package graph

import (
    "fmt"
    "sync"

    "github.com/cheekybits/genny/generic"
)

// Item the type of the binary search tree
type Item generic.Type

// Node a single node that composes the tree
type Node struct {
    value Item
}

func (n *Node) String() string {
    return fmt.Sprintf("%v", n.value)
}

// ItemGraph the Items graph
type ItemGraph struct {
    nodes []*Node
    edges map[Node][]*Node
    lock  sync.RWMutex
}

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

// AddEdge adds an edge to the graph
func (g *ItemGraph) AddEdge(n1, n2 *Node) {
    g.lock.Lock()
    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)
    g.lock.Unlock()
}

// AddEdge adds an edge to the graph
func (g *ItemGraph) String() {
    g.lock.RLock()
    s := ""
    for i := 0; i < len(g.nodes); i++ {
        s += g.nodes[i].String() + " -> "
        near := g.edges[*g.nodes[i]]
        for j := 0; j < len(near); j++ {
            s += near[j].String() + " "
        }
        s += "\n"
    }
    fmt.Println(s)
    g.lock.RUnlock()
}

Quite straightforward. Here’s a test that when run, will populate the graph and print

非常简单。 这是一个测试,运行时将填充图形并打印

$ go test
A -> B C D
B -> A E
C -> A E
D -> A
E -> B C F
F -> E

PASS
package graph

import (
    "fmt"
    "testing"
)

var g ItemGraph

func fillGraph() {
    nA := Node{"A"}
    nB := Node{"B"}
    nC := Node{"C"}
    nD := Node{"D"}
    nE := Node{"E"}
    nF := Node{"F"}
    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)
}

func TestAdd(t *testing.T) {
    fillGraph()
    g.String()
}

遍历图形:BFS (Traversing the graph: BFS)

BFS (Breadth-First Search) is one of the most widely known algorithm to traverse a graph. Starting from a node, it first traverses all its directly linked nodes, then processes the nodes linked to those, and so on.

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

It’s implemented using a queue, which is generated from my Go implementation of a queue data structure with code generation for the node type:

它是使用队列实现的,该队列是从我对队列数据结构的Go实现中生成的,并为节点类型生成了代码:

// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny
package graph

import "sync"

// NodeQueue the queue of Nodes
type NodeQueue struct {
    items []Node
    lock  sync.RWMutex
}

// New creates a new NodeQueue
func (s *NodeQueue) New() *NodeQueue {
    s.lock.Lock()
    s.items = []Node{}
    s.lock.Unlock()
    return s
}

// Enqueue adds an Node to the end of the queue
func (s *NodeQueue) Enqueue(t Node) {
    s.lock.Lock()
    s.items = append(s.items, t)
    s.lock.Unlock()
}

// Dequeue removes an Node from the start of the queue
func (s *NodeQueue) Dequeue() *Node {
    s.lock.Lock()
    item := s.items[0]
    s.items = s.items[1:len(s.items)]
    s.lock.Unlock()
    return &item
}

// Front returns the item next in the queue, without removing it
func (s *NodeQueue) Front() *Node {
    s.lock.RLock()
    item := s.items[0]
    s.lock.RUnlock()
    return &item
}

// IsEmpty returns true if the queue is empty
func (s *NodeQueue) IsEmpty() bool {
    s.lock.RLock()
    defer s.lock.RUnlock()
    return len(s.items) == 0
}

// Size returns the number of Nodes in the queue
func (s *NodeQueue) Size() int {
    s.lock.RLock()
    defer s.lock.RUnlock()
    return len(s.items)
}

导线法 (Traverse method)

Here’s the BFS implementation:

这是BFS的实现:

// Traverse implements the BFS traversing algorithm
func (g *ItemGraph) Traverse(f func(*Node)) {
    g.lock.RLock()
    q := NodeQueue{}
    q.New()
    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.lock.RUnlock()
}

which can be tested with

可以用

func TestTraverse(t *testing.T) {
    g.Traverse(func(n *Node) {
        fmt.Printf("%v\n", n)
    })
}

which after being added to our tests, will print the road it took to traverse all our nodes:

在添加到我们的测试中之后,它将打印遍历我们所有节点所花费的道路:

A
B
C
D
A
E
F

创建一个具体的图形数据结构 (Creating a concrete graph data structure)

You can use this generic implemenation to generate type-specific graphs, using

您可以使用以下通用实现来生成特定于类型的图,方法是:

//generate a `IntGraph` graph of `int` values
genny -in graph.go -out graph-int.go gen "Item=int"

//generate a `StringGraph` graph of `string` values
genny -in graph.go -out graph-string.go gen "Item=string"

翻译自: https://flaviocopes.com/golang-data-structure-graph/

golang数据结构

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值