Go语言数据结构-图

图是一种非线性的数据结构,表示多对多的关系。

定义

图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V, E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。
注意:

  1. 线性表和树可以看做特殊的图。
  2. 线性表中我们把数据元素叫元素,树中将数据元素叫结点,在图中数据元素,我们则称之为顶点(Vertex)
  3. 线性表可以没有元素,称为空表;树中可以没有节点,称为空树;但是,在图中不允许没有顶点(有穷非空性)
  4. 线性表中的各元素是线性关系,树中的各元素是层次关系,而图中各顶点的关系是用边来表示(边集可以为空)。

图的分类

无向图

定义

无向图就是图上的边没有方向。
在这里插入图片描述

上图就是一个无向图。该图的顶点集为 V = { 1 , 2 , 3 , 4 , 5 , 6 } ,边集 E = { ( 1 , 2 ) , ( 1 , 5 ) , ( 2 , 3 ) , ( 2 , 5 ) , ( 3 , 4 ) , ( 4 , 5 ) , ( 4 , 6 ) }。在无向图中,边 ( u , v )和边 ( v , u )是一样的,也就是说和方向无关。

分类
无向完全图

在这里插入图片描述
在无向图中,如果任意两个顶点之间都存在边,则称该图为无向完全图。有n个顶点的无向完全图有n(n-1)/2 条边。

无向连通图

在无向图G中,如果从顶点u到顶点v有路径,则称u和v是连通的。

无向图的度

对于无向图G= (V, E), 如果边(v,v’)属于E, 则称顶点v和v’互为邻接点,即(v,v’)与顶点v和v’相关联。顶点v的度是和v相关联的边的数目。如下面这个无向图,顶点A 的度为3。各个顶点度的和=3+2+3+2=10。而此图的边数是5,推敲后发现,边数其实就是各顶点度数和的一半,多出的一半是因为重复两次计数。
在这里插入图片描述

有向图

定义

有向图就是图上的边有方向。
在这里插入图片描述

上图就是一个有向图。通常情况下,有向图中的边用< >表示,无向图中的边用( )。该图的顶点集为 V = { A , B , C , D } ,边集 E = { < B , A > , < B , C > , < C , A > , < A , D > }。在有向图中,边 < u , v >和边 < v , u >是不一样的。表示。

分类
有向完全图

在有向图中,如果任意两个顶点之间都存在方向互为相反的两条边,则称该图为有向完全图。n个顶点的有向完全图含有n*(n-1)条边。
在这里插入图片描述

有向强连通图

在有向图G中,如果对于每一对顶点vi、vj且vi≠vj,从vi到vj和从vj到vi都存在路径,则称G是强连通图
有向图中的极大强连通子图称做有向图的强连通分量
强连通图具有如下定理:一个有向图G是强连通的,当且仅当G中有一个回路,它至少包含每个节点一次。
在这里插入图片描述

如上图所示,图1不是强连通图,图2是强连通图。图2也可以看做是图1的强连通分量。

有向图的度

对于有向图G = (V, E),如果边<v,v’>属于E,则称顶点v邻接到顶点v’,顶点v’邻接自顶点v的边<v,v’>和顶点v, v’相关联。
从顶点v出发的边的数目称为v的出度;到达顶点v的边的数目称为v的入度,顶点v的度=出度+入度。以下面这个有向图为例,
顶点A的入度是2 (从B到A的边,从C到A的边),出度是1(从A到D的边),所以顶点A的度为2+1=3。此有向图的边有4 条,而各顶点的出度和为1+2+1+0=4,各顶点的入度和=2+0+1+1=4
在这里插入图片描述

图的表示方法

邻接表法

邻接表是图的一种链式存储结构。这种存储结构类似于树的孩子链表。
邻接表由表头节点和表节点两部分组成,图中每个顶点均对应一个存储在数组中的表头节点。如果这个表头节点所对应的顶点存在邻接节点,则把邻接节点依次存放于表头节点所指向的单向链表中。
(1)下图所示的就是一个无向图的邻接表结构。从该图可以看出,顶点表的各个结点由data和firstedge两个域表示,data是数据域,存储顶点的信息,firstedge是指针域,指向边表的第一个结点,即此顶点的第一个邻接点。边表结点由adjvex和next两个域组成。adjvex是邻接点域,存储某顶点的邻接点在顶点表中的下标,next则存储指向边表中下一个结点的指针。例如:v1顶点与v0、v2互为邻接点,则在v1的边表中,adjvex分别为v0的0和v2的2。
在这里插入图片描述

(2)下图是使用邻接表存储有向图。值得注意的是,由于有方向的,因此有向图的邻接表分为出边表和入边表(又称逆邻接表),出边表的表节点存放的是从表头节点出发的有向边所指的尾节点;入边表的表节点存放的则是指向表头节点的某个顶点。
在这里插入图片描述

邻接矩阵法(数组法)

图的邻接矩阵(Adjacency Matrix)存储方式是用两个数组来表示图。一个一维数组存储图中顶点的信息,一个二维数组(称为邻接矩阵)存储图中边的信息。
(1)下图是使用邻接矩阵存储无向图。如图所示,设置两个数组,顶点数组为vertex[4] = {v0, v1, v2, v3},边数组arc[4][4]实际上是一个矩阵。对于矩阵的主对角线的值,即arc[0][0]、arc[1][1]、arc[2][2]、arc[3][3]全为0,这是因为顶点上不存在自环的边。通过这个例子可以看出,无向图的邻接矩阵是一个对称矩阵。
在这里插入图片描述

(2)下图是使用邻接矩阵存储有向图。如图所示,设置两个数组,顶点数组为vertex[4] = {v0, v1, v2, v3},边数组arc[4][4]实际上是一个矩阵。对于矩阵的主对角线的值,即arc[0][0]、arc[1][1]、arc[2][2]、arc[3][3]全为0,这是因为顶点上不存在自环的边。通过这个例子可以看出,有向图的邻接矩阵并不是一个对称矩阵。
在这里插入图片描述

(3)有权图表示
在这里插入图片描述

图的基本结构和方法

type Node struct {
	Value string
	In int //入度
	Out int //出度
	Nexts []*Node //由该点发散出去边直接连接的点集数组
	Edges []*Edge //属于该点的边的数组
}

type Edge struct {
	Weight int //权值:一般表示距离
	From *Node //起始点
	To *Node   //指向点
}

type Graph struct {
	Nodes map[*Node]int
	Edges []*Edge
}

二维数组转为图

package main

type Graph struct {
	Nodes map[int]*Node
	Edges []*Edge
}
type Node struct {
	Value string
	In int //入度
	Out int //出度
	Nexts []*Node //由该点发散出去边直接连接的点集数组
	Edges []*Edge //属于该点的边的数组
}
type Edge struct {
	Weight int //权值:一般表示距离
	From *Node //起始点
	To *Node   //指向点
}

func createGraph(matrix [][]int)  {
    graphData := Graph{}
	for i:=0;i<len(matrix);i++{
		from := matrix[i][0]
		to := matrix[i][1]
		weight := matrix[i][2]
		if _,ok := graphData.Nodes[from];ok{
			graphData.Nodes[from] = &Node{Value:from}
        }
		if _,ok := graphData.Nodes[to];ok{
			graphData.Nodes[to] = &Node{Value:to}
		}
		fromNode,_ := graphData.Nodes[from]
		toNode,_ := graphData.Nodes[to]
		tmpEdge := &Edge{
			Weight:weight,
			From:fromNode,
			To:toNode,
        }
		fromNode.Nexts = append(fromNode.Nexts,toNode)
		fromNode.Out++
		toNode.In++
		fromNode.Edges = append(fromNode.Edges,tmpEdge)
		graphData.Edges = append(fromNode.Edges,tmpEdge)
    }
}

图的遍历

图的宽度优先遍历

思路:

  1. 利用队列实现
  2. 从源节点开始依次按照宽度进队列,然后弹出
  3. 每弹出一个点,把该节点所有没有进过队列的邻接点放入队列
  4. 直到队列变空

代码实现:

func BFS(aNode *Node) {
	if aNode == nil {
		return
	}
	nodeQueue := []*Node{aNode}
	nodeMap := map[*Node]int{aNode: 1}
	for len(nodeQueue) > 0 {
		cur := nodeQueue[0]//取index最小的元素
		nodeQueue = nodeQueue[1 : len(nodeQueue)]
		fmt.Println(cur.Value)
		for _,next := range cur.Nexts{
			if _,ok := nodeMap[next];!ok {
				nodeQueue = append(nodeQueue,next)
				nodeMap[next] = 1
            }
        }
	}

}

图的广度优先遍历

思路:

  1. 利用栈实现
  2. 从源节点开始把节点按照深度放入栈,然后弹出
  3. 每弹出一个点,把该节点下一个没有进过栈的邻接点放入栈
  4. 直到栈变空

代码实现:

func DFS(aNode *Node) {
	if aNode == nil {
		return
	}
	nodeStack := []*Node{aNode}
	nodeMap := map[*Node]int{aNode: 1}
	for len(nodeStack) > 0 {
		cur := nodeStack[len(nodeStack)-1] //取index最大的元素
		nodeStack = nodeStack[0 : len(nodeStack)-1]
		fmt.Println(cur.Value)
		for _,next := range cur.Nexts {
			if _,ok := nodeMap[next];!ok {
				nodeStack = append(nodeStack,cur,next)
				nodeMap[next] = 1
				fmt.Println(next.Value)
				break
            }
        }
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值