【图论】一、图论入门、分类和建图
图是数据结构中的一种,电脑中的数据可以使用图的形式来存放。日常中大家使用的地图也是图的一种,包含了各种景区和景区之间的路线。例如分析走完所有景区的最短路程就是我们图论问题中的一种。
图的基本概念
我们平时在工作或是比赛中用到的图论包含以下几种最基础的基本概念:
- 顶点(Vertex):每一个顶点都可以包含元素和权值,通常用u、v来表示。
- 边(Edge):连接两个顶点的线段,可以是有向的(箭头表示方向),也可以是无向的。
- 权重(Weight):边可以具有权重,表示从一个顶点到另一个顶点的“代价”或“距离”。
- 路径(Path):一条联通的路径,可以从一个顶点经过几个顶点到达终点。
- 环(Cycle):在一个路径中,如果第一个顶点和最后一个顶点相同,则该路径形成一个环。
- 子图(Subgraph):原图中顶点和边的集合,它们自己也可以形成一个图。
- 连通图(Connected Graph):在无向图中,如果任意两个顶点之间都存在路径,则该图是连通的。
- 强连通图(Strongly Connected Graph):在有向图中,如果任意两个顶点之间都存在双向路径,则该图是强连通的。
- 连通分量(Connected Components):在非连通图中,每一个极大连通子图称为一个连通分量。
- 度(Degree):顶点的度是指与该顶点相连的边的数量。在有向图中,还可以细分为入度和出度。
- 最短路径(Shortest Path):在加权图中,从一个顶点到另一个顶点的权重和最小的路径。
上图就是一张共6顶点8条边的带权连通图,其中1、5、6顶点构成一个环,5、6、2和1、5、6、2顶点也是两个环。3-5-2是一条路径,2-5-3-4也是一条2顶点到4顶点的路径。2到4的最短路径就是2-5-3-4,权值为7。相比而言2-6-1-3-4这条路径权值为13,显然大于前者。1、5、6、2可以构成1、2、3、4、5、6的一个子图,并且也是联通的。顶点2的度为2,顶点5的度为4。
图的构建
比赛中常见的建图方式有三种,包含:邻接矩阵、邻接表、链式前向星(有人也叫前矢链向星,指我)。
邻接矩阵
建立一个数组: a [ u ] [ v ] = w a[u][v] = w a[u][v]=w,代表顶点 u u u和 v v v中间有一条边,权值为 w w w。
邻接矩阵空间复杂度高,添加和删除点成本高,但构建容易。适合那种点不多不用删改点的题。
邻接表
邻接表是一种基于链表的存储方式,它将每个顶点的所有邻接点存储在一个链表中。具体来说,邻接表由一个顶点数组和一个边链表组成,顶点数组中的每个元素表示一个顶点,边链表中的每个结点表示一条边。不太实用。
链式前向星
建立一个结构体,起名为
e
d
g
e
edge
edge,每一条边主要包含元素
n
e
x
t
next
next(同起点的上一条边),
t
o
to
to(终点),
w
w
w(权值)。
建立一个数组,
h
e
a
d
[
i
]
=
x
head[i] = x
head[i]=x,代表起点为
i
i
i的最后一条边的序号。
const int maxn = 2e5 + 5;
int cnt; //cnt的数量代表边的数量
struct edge { //定义结构体,表示一条边
int next, to; //next是遍历时需要用到的下一条边的序号,to是边的终点结点
int w; //w是权值
};
int head[maxn]; //存放每个结点的首条边序号,就是在创建这条边时的cnt值
edge e[maxn];
void init(int n) { //初始化,这步不可少
cnt = 0;
for(int i = 0; i <= n; i++) {
head[i] = -1;
e[i].next = -1;
}
}
void add(int u, int v, int w) { //添加边
e[cnt].to = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt++;
}
void fore(int n) {
for(int i = 1; i <= n; i++) { //按顶点1到n进行遍历边
for(int j = head[i]; j > -1; j = e[j].next) {
cout << "u: " << i << ' ' << "v: " << e[j].to << ' ' << "w: " << e[j].w << '\n';
}
}
}