数据结构——图

下面来学习一下数据结构里面的图
首先来看看这一章的主要内容框架



		|- 图的定义			|- 邻接矩阵
		|					|- 邻接表
		|- 图的存储结构 -----|- 十字链表、邻接多重表
		|-----|- 图的遍历 -----|- 深度优先遍历
		|				|- 广度优先遍历
		|
		|- 图的相关应用 -----|- 最小生成树:Prim算法,Kruskal算法
							|- 最短路径:Dijkstra算法,Floyd算法
							|- 拓扑排序:AOV网
							|- 关键路径:AOE网

一、图的基本概念

1. 图的定义
(1)有向图

有向图的边也称为弧,弧是顶点的有序对,记为 <v, w>
在这里插入图片描述

(2)无向图

无向图的边是顶点的无序对,记为 (v, w)

(3)简单图

① 不存在重复边; ② 不存在顶点到自身的边

(4)多重图

两个结点的边多于一条,又允许顶点通过同一条边和自己关联。(与简单图相对)

(5)完全图

无向图:任意两个顶点之间都存在边,n 个顶点有 n(n-1)/2 条边;
有向图:任意两个顶点之间都存在方向相反的两条弧,n 个顶点有 n(n-1) 条弧;

(6)子图

两个图 G=(V, E) 和 G1=(V1, E1),V1是V的子集,E1是E的子集,则称G1是G的子图。
若有满足 V(G1)=V(G)的子图G1,则为G的生成子图。

(7)连通、连通图、连通分量(对无向图)
  • 连通:v 到 w 有路径存在,则称 v 和 w 是连通的;
  • 连通图:图中任意两个顶点都是连通的,则称为连通图;
  • 连通分量:无向图中极大连通子图称为连通分量;
    (极大:要求该连通子图包含其所有的边);
    (极小连通子图:既要保持连通,又要使得边数最少的子图);
    如果图有 n 个顶点,n-1 条边,则必为非连通。
(8)强连通图、强连通分量(对有向图)
  • 强连通:v 到 w 和 w 到 v 都有路径存在,则称 v 和 w 是强连通的;
  • 强连通图:图中任意两个顶点都是强连通的,则称为强连通图;
  • 强连通分量:有向图中极大强连通子图称为强连通分量;
(9)生成树、生成森林
  • 连通图,生成树是包含图中全部顶点的一个极小连通子图;
    (生成树有 n 个顶点 n-1 条边,减去一边则变非连通,增加一边则形成回路)
  • 非连通图中,连通分量的生成树构成了非连通图的生成森林;
(10)顶点的度、入度、出度
  • 度:无向图中,某顶点连接的边的条数;
    (无向图:全部顶点度数之和 = 2 * 边数)
  • 入度:有向图中,指向某顶点的边的条数;
  • 出度:有向图中,从某顶点射出的边的条数;
    (有向图:全部顶点的 出度 + 入度 = 边数)
(11)边的权和网

网:即为带权图

(12)稀疏图、稠密图

一般满足 |E| < |V|*log|V|,为稀疏图;反之为稠密图。

(13)路径、路径长度、回路
  • 路径长度:路径上的边数;
  • 回路:也称环,n 个顶点,多于 n-1 条边,则一定有环;
(14)简单路径、简单回路
  • 简单路径:路径序列中,顶点不重复出现;
  • 简单回路:除第一和最后一个顶点,其余顶点不重复出现;
(15)距离

从 v 出发到 w 的最短路径若存在,称为从 v 到 w 的距离;若不存在,则距离为无穷。

(16)有向树

有一个顶点入度为0,其余顶点入度均为1的有向图。

二、图的存储及基本操作

存储方式存储结构适合的图是否唯一适用有向或无向
邻接矩阵顺序存储稠密图唯一有向图、无向图
邻接表链接存储稀疏图不唯一有向图、无向图
十字链表链式存储稀疏图不唯一有向图
邻接多重表链式存储稀疏图不唯一无向图
1. 邻接矩阵法

(邻接矩阵存储比较简单,这里就不画图了。有边记1,无边记0,带权值的写权重。)

//邻接矩阵存储结构定义
#define MaxVertexNum 100		//顶点最大数目
typedef char VertexType;		//顶点数据类型
typedef int EdgeType;			//边上权值数据类型
typedef struct{
	VertexType Vex[MaxVertexNum];					//顶点表
	EdgeType Edge[MaxVertexNum][MaxVertexNum];		//邻接矩阵,边表
	int vexnum, arcnum;								//图当前顶点数、弧数
}MGraph;

图的邻接矩阵存储具有以下特点
(1)邻接矩阵表示法空间复杂度为 O(n);
(2)优点:容易确定任意两个顶点之间是否有边;
(3)缺点:要确定图中有多少条边,则必须按行列检测,花费时间较大;
(4)稠密图适合用邻接矩阵。

2. 邻接表法

顶点表、边表:
在这里插入图片描述

//邻接表存储结构定义
#define MaxVertexNum 100			//顶点最大数目
typedef struct ArcNode{				//边表节点
	int adjvex;						//该弧所指向的顶点位置
	struct ArcNode *next;			//指向下一条弧的指针
}ArcNode;							
typedef struct VNode{				//顶点表节点
	VertexType data;				//顶点信息
	ArcNode *first;					//指向第一条依附该点的弧的指针
}VNode, AdjList[MaxVertexNum];		
typedef struct{
	AdjList vertices;				//邻接表
	int vexnum, arcnum;				//图顶点数、弧数
}ALGraph;							//ALGraph是以邻接表存储的图类型

图的邻接表存储具有以下特点
(1)存储空间:无向图 O(|V|+2|E|);有向图 O(|V|+|E|);
(2)稀疏图,邻接表存储可以极大地节省存储空间;
(3)优点:给定一个顶点,容易找到它的所有邻边;
(4)缺点:如果要确定给定两个顶点之间是否存在边,则需要在相应节点的对应的边表里查找另一节点,效率较低;
(5)出度:给定节点的出度,计算其邻接表中节点的个数;
(6)入度:需要遍历整个邻接表,(也有采用逆邻接表来加速求解入度)

3. 十字链表

弧节点、顶点节点:
在这里插入图片描述

//十字链表存储结构定义
#define MaxVertexNum 100			//顶点最大数目
typedef struct ArcNode{				//边表节点
	int tailvex, headvex;			//该弧的头尾节点
	struct ArcNode *hlink, *tlink;	//分别指向弧头相同和弧尾相同的节点
}ArcNode;							
typedef struct VNode{				//顶点表节点
	VertexType data;				//顶点信息
	ArcNode *firstin, *firstout;	//指向第一条入弧和出弧
}VNode;		
typedef struct{
	VNode xlist[MaxVertexNum];		//邻接表
	int vexnum, arcnum;				//图顶点数、弧数
}GLGraph;							//GLGraph是以十字链表存储的图类型
4. 邻接多重表

在这里插入图片描述

//邻接多重表存储结构定义
#define MaxVertexNum 100			//顶点最大数目
typedef struct ArcNode{				//边表节点
	bool mark;						//访问标记
	int ivex, jvex;			//分别指向该弧的两个结点
	struct ArcNode *ilink, *jlink;	//分别指向两个结点的下一条边
}ArcNode;							
typedef struct VNode{				//顶点表节点
	VertexType data;				//顶点信息
	ArcNode *firsedge;	//指向第一条依附该顶点的下一条边
}VNode;		
typedef struct{
	VNode adjmulist[MaxVertexNum];	//邻接表
	int vexnum, arcnum;				//图顶点数、弧数
}AMLGraph;							//AMLGraph是以十字链表存储的图类型
5. 图的基本操作
函数名称操作
Adjacent(G, x, y)判断图G中是否存在边<x, y>或(x, y)
Neighbors(G, x)列出图G中与节点x邻接的边
InsertVertex(G, x)在图G中插入顶点x
DeleteVertex(G, x)在图G中删除顶点x
AddEdge(G, x, y)给图G中添加边<x, y>或(x, y)
RemoveEdge(G, x, y)从图G中删除边<x, y>或(x, y)
FirstNeighbor(G, x)求图G中顶点x的第一个邻接点,有则返回顶点号
NextNeighbor(G, x, y)假设图G中y是x的一个邻接点,返回出y之外顶点x的下一个邻接点的顶点号
Get_edge_value(G, x, y)获取图G中边<x, y>或(x, y)的权值
Set_edge_value(G, x, y)设置图G中边<x, y>或(x, y)的权值

三、图的遍历

图的遍历是指从图中某一顶点出发,按照某种方法将所有节点访问一次且仅访问一次。

搜索方式类似于二叉树的遍历借助工具是否递归
广度优先搜索BFS层次遍历队列不递归
深度优先搜索DFS先序遍历递归

注意:基于邻接矩阵的BFS和DFS是唯一的,基于邻接表的BFS和DFS是不唯一的。

1. 广度优先搜索(BFS)
(1)BFS基本概念

是一个分层的查找过程,不是递归算法,需要借助一个队列,用来存放正在访问的顶点的下一层顶点。

//广度优先搜索

下面通过实例来解释一下广度搜索的过程:

(2)BFS算法性能分析
  • 无论邻接表还是邻接矩阵:都需要申请辅助队列Q,n 个顶点都要入队一次,最坏情况下,空间复杂度 O(|V|);
  • 邻接表:时间复杂度 O(|V|+|E|);
  • 邻接矩阵:时间复杂度 O(|V|^2);
(3)BFS求解单源点最短路径问题

对于非带权图,顶点 v 到 w 的边数为最短路径,可以用BFS求解。(这是由广度优先搜索总是按照距离由近到远来遍历图中每个顶点的性质决定的)

//BFS算法求解单源点最短路径问题
(4)广度优先生成树

广度优先遍历中,我们可以得到一棵遍历树,称为广度优先生成树。(不唯一)
图的广度优先生成树比深度优先生成树的 高度 小或相等。

2. 深度优先搜索(DFS)
(1)DFS基本概念

类似于树的先序遍历,是递归的,可以判断图中是否有回路。

//深度优先遍历算法
(2)DFS算法性能分析
  • 需要借助一个栈,空间复杂度 O(|V|);
  • 邻接表,时间复杂度 O(|V|+|E|);
  • 邻接矩阵,时间复杂度 O(|V|^2);
(3)深度优先生成树和生成森林

对连通图调用DFS产生深度优先生成树,对非连通图产生深度优先生成森林。

3. 图的遍历和图的连通性
  • 对于无向图,调用 BFS(G, i) 或 DFS(G, i) 的次数 = 该图的连通分量数;
  • 对于有向图,则不是;因为一个连通的有向图分为强连通和非强连通,非强连通分量依次调用 BFS(G, i) 或 DFS(G, i) 不能访问到该连通分量的所有顶点。

四、图的应用


注意:这里简单介绍一下图的应用,具体每个算法的计算过程在另一篇博客中详细讲解。(点击下面的链接)图的应用算法详解


应用算法适用的图备注
最小生成树Prim无向图适合稠密图
最小生成树Kruskal无向图适合稀疏图
最短路径Dijkstra有向图单源点最短路径
最短路径Floyd有向图每一对顶点之间的最短路径
拓扑排序/有向无环,AOV网
关键路径/有向无环加权,AOE网
1. 最小生成树(MST)
  • 对于一个带权连通无向图,生成树不同,每棵树的权也可能不同。其中权值之和最小的那棵树称为最小生成树。
  • 最小生成树的特点:
    (1)不唯一;
    (2)边的权值之和唯一,而且是最小的;
    (3)边数 = 顶点数 - 1;
(1)Prim算法

时间复杂度 O(|V|^2)

(2)Kruskal算法

时间复杂度 O(|E|log|E|)

2. 最短路径
  • 带权路径长度:对于带权图,从一个点 v0 到 其余各点 vi 的一条路径上所经过边的权值之和;
  • 最短路径:min 带权路径长度。
(1)Dijkstra算法求单源点最短路径问题

时间复杂度 O(|V|^2);
如果要找出所有节点对之间的距离,则时间复杂度 O(|V|^3)

(2)Floyd算法求各顶点之间最短路径问题

时间复杂度 O(|V|^3)

3. 拓扑排序

有向无环(AOV网)
可以判断图中是否有回路。
时间复杂度 O(|V|+|E|)

4. 关键路径

带权有向无环(AOE网)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值