图论基础

图的定义
  1. 图G是一个有序二元组(V,E),其中V称为顶点集(Vertices Set),E称为边集(Edges set),E与V不相交。它们也可写成V(G)和E(G)。E的元素都是二元组,代表一条边的两个端点,用(x,y)表示,其中x,y∈V

  2. 图G是一个三元组(V,E,I),其中V称为顶集,E称为边集,I称为关联函数。I将E中的每一个元素映射到 V×V。如果e被映射到(u,v),那么称边e连接顶点u,v,而u,v则称作e的端点,u,v此时关于e相邻。同时,若两条边i,j有一个公共顶点u,则称i,j关于u相邻

图的概念

子图(Sub-Graph):这个概念类似于集合的子集。当图G’=(V’,E’)其中V’包含于V,E’包含于E,则G’称作图G=(V,E)的子图,每个图都是本身的子图

度(Degree):一个顶点的度是指与该顶点相关联的边的条数,顶点v的度记作d(v)。通常对于不包含自环的图来说,所有点度的和等于边数的二倍,即∑v∈V d(v) = 2*E。对于有向图来说,指向某点的边数之和称为该点的入度,以该点为起点的边数之和称为该点的出度

路径(Path):从顶点 s 到顶点 t 的一条路径是指一个序列 v0, e1, v1, e2, v2, …ek , vk,ei 的起点终点为 vi−1及 vi;k 称作路径的长度;v0 = s,称为路径的起点;vk = t,称为路径的终点。如果 s = t,称该路径是闭的,反之则称为开的

回路(Circuit):闭的路径

图的常见分类

无向图(Undirected graphs):图中任意两个顶点之间的边都是无向边,即E(u,v) = E(v,u)

有向图(Directed graphs):图中任意两个顶点之间的边都是有向边,即E<u,v> ≠ E<v,u>

有权图:图的边上都带有权值

无权图:图的边上没有权值

简单图:图中不存在顶点到其自身的边,且同一条边不重复出现

无向完全图:无向图中,任意两个顶点之间都存在边。若顶点为n个,则边数为n(n-1)/2

有向完全图:有向图中,任意两个顶点之间都存在方向互为相反的两条弧。若顶点为n个,则边数为n(n-1)

稀疏图:有很少条边

稠密图:有很多条边

图的存储方式

1.邻接矩阵

存储:int G[N][N](声明 N × N 的数组)

内容:G[i][j] 表示节点 i 到 j 的边权,通常我们依据题目背景选定一个值来作为无边的标识,如采用 0 或 ±∞

性质:有向图中,G[i][j] ≠ G[j][i],无向图中,G[i][j] = G[j][i]

优点:适合稠密图,易于实现、维护

缺点:空间复杂度 O(V2) 太高,且对重边的处理有较大局限性

在这里插入图片描述

2.邻接表

存储:链表或 vector 数组,一般使用vector数组vector<edge> G[N];

内容:存下每个点的出边

优点:复杂度为 O(V+E),能存重边,易于操作邻边集

缺点:实现略微复杂,访问和修改会慢一点,因为找每个点的某个邻接点时需要线性遍历

下图以有向图的邻接表为例:

在这里插入图片描述

struct edge{
	int from,to,w; //from始点,to终点,w权值
    edge(){}
    edge(int a,int b,int c){ from=a; to=b; w=c; }
};

vector<edge> G[N];

int main(){
    //初始化
    for(int i=1;i<=n;i++) G[i].clear();
    //存边
    G[?].push_back(edge(a,b,c));
    return 0;
}

3.链式前向星

图的连通性

连通图

无向图 G 中,若对任意两点i,j,从顶点 Vi 到顶点 Vj有路径,则称 Vi和 Vj 是连通的,则图 G 是连通图

强连通图

有向图 G 中,若对任意两点,从顶点 Vi 到顶点 Vj,都存在从 Vi 到 Vj 以及从 Vj 到 Vi 的路径,则称 G 是强连通图

判断连通性

无向图:由于无向图的边是双向的,如果存在路径 Vi 到 Vj,那么必然有路径 Vj 到 Vi。因此,对任意一个点判断其连通性,若其能与该图中所有其它点连通,则该图为连通图。跑一遍 DFS 或 BFS 都可以,此外还可以用并查集判断

有向图:使用点的连通性的判断方法,对每个点都判断其连通集合是否为该点外所有的点的集合即可

图的遍历

DFS

vector<int> G[N];
bool vis[N];
void DFS(int v){
	//省略了边界特判
    vis[v]=true;
	for(int i = 0;i<G[v].size();i++){
		if(!vis[G[v][i]])
			dfs(G[v][i]);
	}
	//省略了处理
}

BFS

vector<int> G[N];
queue<int> q;
bool vis[N];
void BFS(int s){
	vis[s]=true;
	q.push(s);
	int t;
	while(!q.empty) {
		t=q.front();
		q.pop();
		for (int i=0;i<G[t].size();i++) {
			if (!vis[G[t][i]]){
				vis[G[t][i]]=true;
				q.push(G[t][i]);
				//省略了处理
			}
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值