设为首页
收藏本站
切换到窄版
登录
立即注册
找回密码
搜索
搜索
本版
帖子
用户
快捷导航
论坛
BBS
C语言
C++
NET
JAVA
PHP
易语言
数据库
IE盒子
»
论坛
›
IE盒子
›
C++
›
C++ 数据结构提高
返回列表
发帖
查看:
114
|
回复:
0
C++ 数据结构提高
[复制链接]
神不在的世界
神不在的世界
当前离线
积分
21
4
主题
11
帖子
21
积分
新手上路
新手上路, 积分 21, 距离下一级还需 29 积分
新手上路, 积分 21, 距离下一级还需 29 积分
积分
21
发消息
发表于 2023-1-5 18:31:15
|
显示全部楼层
|
阅读模式
1.1 图的定义和相关术语
什么是图?直白地说,就是类似于地图的东西,例如下图展示的学生生活路线就是一个图。
抽象出来看,图由顶点(Vertex)和边(Edge)组成,每条边的两端都必须是图的两个顶点(可以是相同的顶点)。而记号G(V,E)表示图G的顶点集为V、边集为E。下图是一个抽象出来的图。
一般来说,图可分为
有向图
和
无向图
。
有向图的所有边都有方向,即确定了顶点到顶点的一个指向;而无向图的所有边都是双向的,即无向边所连接的两个顶点可以互相到达。
在一些问题中,可以把无向图当作所有边都是正向和负向的两条有向边组成,这对解决一些问题很有帮助。
下图是有向图和无向图的举例。
顶点的
度
是指和该顶点相连的边的条数。
特别是对于有向图来说,顶点的出边条数称为该顶点的出度,顶点的入边条数称为该顶点的
入度
。例如上图的无向图中,V1 的度为 2 ,V2 的度为4;有向图例子中,V2 的出度为 1 、入度为 2 。
顶点和边都可以有一定属性,而量化的属性称为
权值
,顶点的权值和边的权值分别称为点权和边权。
权值可以根据问题的实际背景设定,例如点权可以是城市中资源的数目,边权可以使两个城市之间来往所需要的时间或花费。
1.2 图的存储
一般来说,图的存储方式有两种∶邻接矩阵和邻接表。这两种存储方式各有优势,需要在不同的情况下选择使用。
1.2.1 邻接矩阵
设图 G(V,E) 的顶点标号为 0,1,…,N-1,那么可以令二维数组 G[N][N] 的两维分别表示图的顶点标号,即如果 G
[j] 为1,则说明顶点 i 和顶点 j 之间有边;如果 G
[j] 为 0,则说明顶点 i 和顶点 j 之间不存在边,而这个二维数组 G[][] 则被称为邻接矩阵。
另外,如果存在边权,则可以令 G
[j] 存放边权,对不存在的边可以设边权为 0、-1 或是一个很大的数。
下图是一个作为举例的无向图以及对应的邻接矩阵(边权为 0 表示不存在边),显然对无向图来说,邻接矩阵是一个对称矩阵。
虽然邻接矩阵比较好写,但是由于需要开一个二维数组,如果顶点数目太大,便可能会超过题目限制的内存。因此邻接矩阵只适用于顶点数目不太大(一般不超过 1000 )的题目。
1.2.2 邻接表
设图 G(V,E) 的顶点编号为 0,1,…,N-1,每个顶点都可能有若干条出边,如果把同一个顶点的所有出边放在一个列表中,那么 N 个顶点就会有 N 个列表(没有出边,则对应空表)。
这 N 个列表被称为图 G 的邻接表,记为 Adj[N] ,其中 Adj
存放顶点i的所有出边组成的列表,这样Adj[0] ,Adj[1],Adj[N-1] 就分别都是一个列表。
由于列表可以用链表实现,如果画出上图对应的邻接表,就会得到图上图。
其中 Adj[0] 用链表连接了两个结点,每个结点存放一条边的信息(括号外的数字是边的终点编号,括号内的数字是边权),于是 0 号顶点有两条出边:
一条的终点为 1 号顶点(边权为 2 );
另一条边的终点为4号顶点(边权为 1 )。
而对 Adj[4] 来说,它表示 4 号顶点的三条出边的信息,这三条出边的终点分别是 0 号顶点、1 号顶点、3 号顶点,边权分别为1、2、1。
对初学者来说可能会不太容易很快就熟练使用链表来实现邻接表,因此此处介绍另一种更为简单的工具来实现邻接表:vector ,它能让初学者更快上手并易于使用,且不易出错。
由于 vector 有变长数组之称,因此可以开一个 vector 数组 Adj[N] ,其中 N 为顶点个数。这样每个 Adj
就都是一个变长数组 vector ,使得存储空间只与图的边数有关。
如果邻接表只存放每条边的终点编号,而不存放边权,则 vector 中的元素类型可以直接定义为 int 型,如下所示:
vector<int> Adj[N];
下图为把上图中的邻接表采用 vector 数组进行存储的情况(只存放边的终点编号)。
如果想添加一条从 1 号顶点到达 3 号顶点的有向边,只需要在 Adj[1] 中添加终点编号 3 即可,代码如下所示(如果是无向边,就再添加一条从 3 号顶点到达 1 号顶点的有向边):
Adj[1].push_back (3);如果需要同时存放边的终点编号和边权,那么可以建立结构体 Node ,用来存放每条边的终点编号和边权,代码如下所示:
struct Node
{
int V; //边的终点编号
int w; //边权
};
这样 vector 邻接表中的元素类型就是 Node 型的,如下所示:
vector<Node> Adj [N];
此时如果想要添加从 1 号到达 3 号顶点的有向边,边权为 4 ,就可以定义一个 Node 型的临时变量 temp ,令 temp.v=3、temp.w=4,然后把 temp 加入到 Adj[1] 中即可,代码如下所示:
Node temp;
temp.v = 3;
temp.w = 4;
Adj[1].push_back(temp);
当然,更快的做法是定义结构体 Node 的构造函数,代码如下所示:
struct Node
{
int v,w;
Node(int _v,int _w):V(_v),w(_w) {} //构造函数
}
这样就能不定义临时变量来实现加边操作,代码如下所示:
Adj[1].push back(Node (3,4));
于是就可以使用 vector 来很方便地实现邻接表,在些顶点数目较大(一般顶点个数在1000 以上)的情况下,一般都需要使用邻接表而非邻接矩阵来存储图。
回复
使用道具
举报
返回列表
发帖
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
|
立即注册
本版积分规则
发表回复
回帖后跳转到最后一页
浏览过的版块
NET
快速回复
返回顶部
返回列表