数据结构-图(上)-golang

mb5fe190725e8a3 · · 697 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

图片

    继续我们的总结回顾,图的算法难度较大,理论知识也非常多,倘若是考研党的话,应掌握图的基本概念及相关性质等。我本人翻看了下教材,考虑到文字信息实在太多,所以我打算以模拟大纲形式跟大家梳理下内容,细化知识点就需要大家自行掌握记忆了。

















知识框架

图片


图片



图的基本概念



1图的定义


01 - 有向图

02 - 无向图

03 - 简单图

04 - 多重图

05 - 完全图

06 - 子图

07 - 连通、连通图和连通分量

08 - 强连通图、强连通分量

09 - 生成图、生成森林

10 - 顶点的图、入度和出度

11 - 边的权和网

12 - 稠密图、稀疏图

13 - 路径、路径长度和回路

14 - 简单路径、简单回路

15 - 距离

16 - 有向树



图的存储及基本操作


1邻接矩阵法


图片

2邻接表法


图片


3十字链表法


image.png


4邻接多重表


image.png


5图的基本操作


    图的基本操作是独立于图的存储结构的。而对于不同的存储方式,操作算法的具体实现会有着不同的性能。在设计具体算法的实现时,应考虑采用何种存储方式的算法效率会更高。

图片

//此程序用来建立图,输出图golang实现
package main

import (
  "fmt"
  "github.com/cheekybits/genny/generic"   //导入第三方包定义一个新的类型
)

type Item generic.Type

//Node struct  define
//结点定义
type Node struct {
  Value Item
}

//Graph define
//图的定义
type Graph struct {
  nodes []*Node          //结点集合,是一个切片类型,为指向Node结构体的指针
  edges map[Node][]*Node //邻接表表示的无向图
}


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

//AddNode method
//添加结点
//append用来将元素添加到切片末尾并返回结果,append(切片类型,其他的元素)
func (g *Graph) AddNode(n *Node) {
  g.nodes = append(g.nodes, n)
}

//AddEdge method
//增加边信息
func (g *Graph) AddEdge(u, v *Node) {
  //不存在图时,创建一个图
  if g.edges == nil {
    g.edges = make(map[Node][]*Node)
  }
  g.edges[*u] = append(g.edges[*u], v) //创建u->v的边
  g.edges[*v] = append(g.edges[*v], u) //无向图,也存在v->u的边
}

//格式化输出图
func (g *Graph) String() {
  str := ""
  for _, node := range g.nodes {
    str += node.String() + "->"
    nexts := g.edges[*node]
    for _, next := range nexts {
      str += next.String() + " "
    }
    str += "\n"
  }
  fmt.Println(str)
}

func main() {
  g := Graph{}
  n1, n2, n3, n4, n5 := Node{Value:1}, Node{Value:2}, Node{Value:3}, Node{Value:4}, Node{Value:5}

  g.AddNode(&n1)
  g.AddNode(&n2)
  g.AddNode(&n3)
  g.AddNode(&n4)
  g.AddNode(&n5)

  g.AddEdge(&n1, &n2)
  g.AddEdge(&n1, &n5)
  g.AddEdge(&n2, &n3)
  g.AddEdge(&n2, &n4)
  g.AddEdge(&n2, &n5)
  g.AddEdge(&n3, &n4)
  g.AddEdge(&n4, &n5)

  g.String()
}



图的遍历


1广度优先遍历(BFS)


01 - 算法原理


    广度优先搜索(Breadth-First-Search BFS)类似于二叉树的层序遍历算法。基本思想是:首先请问起始顶点v,接着由v出发,依次访问v的各个未访问过的邻接顶点W1,W2,W3........Wi然后依次访问W1W2,W3........Wi的所有未被访问过的邻接顶点;再从这些访问过的顶点出发,访问它们所有未被访问过的邻接顶点,直至图中所有顶点都被访问过为止。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作为始点,重复上述过程,直至图中所有顶点都被访问到为止。

    换句话说,广度优先搜索遍历图的过程是以v为起始点,由近至远依次访问和ν有路径相通且路径长度为1,2,...的顶点。广度优先搜索是一种分层的查找过程,每向前走一步可能访问一批顶点,不像深度优先搜索那样有往回退的情况,因此它不是一个递归的算法。为了实现逐层的访问,算法必须借助一个辅助队列,以记忆正在访问的顶点的下一层顶点。


02 - 代码实现(golang)


//此程序是BFS算法的golang实现,所实现的图是上一节生成的图
package graph

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

type Item generic.Type

//Node struct  define
//结点定义
type Node struct {
  Value Item
}

//Graph define
//图的定义
type Graph struct {
  nodes []*Node          //结点集合,是一个切片类型,为指向Node结构体的指针
  edges map[Node][]*Node //邻接表表示的无向图
}

//结点定义
type NodeQueue struct {
    nodes []Node
    
}

// 实现 BFS 遍历
func (g *Graph) BFS(f func(node *Node)) {

    // 初始化队列
    q := NewNodeQueue()
    // 取图的第一个节点入队列
    head := g.nodes[0]
    q.Enqueue(*head)
    // 标识节点是否已经被访问过
    visited := make(map[*Node]bool)
    visited[head] = true
    // 遍历所有节点直到队列为空
    for {
        if q.IsEmpty() {  //队列为空直接退出
            break
        }
        node := q.Dequeue()   //结点出队列
        visited[node] = true  //将该出队列结点标记为已访问true
        nexts := g.edges[*node]  
        // 将所有未访问过的邻接节点入队列
        for _, next := range nexts {
            // 如果节点已被访问过
            if visited[next] {
                continue
            }
            q.Enqueue(*next)
            visited[next] = true
        }
        // 对每个正在遍历的节点执行回调
        if f != nil {
            f(node)
        }
    }
}

// 生成节点队列
func NewNodeQueue() *NodeQueue {
    q := NodeQueue{}   
    q.nodes = []Node{}
    return &q
}

// 入队列
func (q *NodeQueue) Enqueue(n Node) {
    q.nodes = append(q.nodes, n)
}

// 出队列
func (q *NodeQueue) Dequeue() *Node {  
    node := q.nodes[0]
    q.nodes = q.nodes[1:]
    return &node
}

// 判空
func (q *NodeQueue) IsEmpty() bool {   
    return len(q.nodes) == 0
}


    不难看出,图的广度优先搜索的过程与二叉树的层序遍历是完全一致的,这也说明了图的广度优先搜索遍历算法是二叉树的层次遍历算法的扩展。


03 - 算法分析


图片


2深度优先遍历(DFS)


01 - 算法原理


    与广度优先搜索不同,深度优先搜索(Depth-First-Search,DFS)类似于树的先序遍历。如其名称中所暗含的意思一样,这种搜索算法所遵循的搜索策略是尽可能“深”地搜索一个图。

    它的基本思想如下:首先访问图中某一起始顶点ν,然后由v出发,访问与v邻接且未被访问的任一顶点W1再访问与W1邻接且未被访问的任一顶点W2......重复上述过程。当不能再继续向下访问时,依次退回到最近被访问的顶点,若它还有邻接顶点未被访问过,则从该点开始继续上述搜索过程,直至图中所有顶点均被访问过为止。


02 - 算法实现


图片


03 - 算法分析


图片



有疑问加站长微信联系(非本文作者)

本文来自:51CTO博客

感谢作者:mb5fe190725e8a3

查看原文:数据结构-图(上)-golang

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

697 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传