图论的定义
图是由若干个顶点和一部分边所构成的图形,这种图形通常用来描述某些事物或人之间的某种特定关系。顶点用于代表事物,连接两顶点的边则用于表示两个事物间具有某种关系。
如图所示:
可以看出1和2有某一种关系,1和3也有某一种关系,1和4也是如此。
当然,这一种关系是由自己来定义的,比如关系代表有联系,那么就是1和2有联系,1和3也有联系,1和4也有联系。
有向图和无向图
有向图(Directed graph)就是在一个图里有箭头,如图。
在上述图片中就和原来的定义不一样了,原来是1和2互相联系,而现在就只能让1联系2,不能让2联系1了。
而无向图(Undirected graph)与最开始的图一样,可以互相联系。
图的遍历
图的遍历有很多种,如邻接表、邻接矩阵还有前向星等等。
我们这里主要说这两种。
邻接矩阵存图:
int G[110][110],u,v,w;
for(int i = 1; i <= n; i++){
cin >> u >> v >> w;
G[u][v] = w;
// G[v][u] = w;
}
如果要建双向边,则G[v][u]也要赋值。
U是这条边的起始地,V是这条边通往的地方,W是这条边的边权。
则G[i][j]表示从 i 到 j 有一条边,边权为 w。
有人会问了,边权(Border rights)是啥?
抽象的理解就是这条边的长度。
如图:
可以看出1到2的路径长度为1,2到3的路径长度为2,3到1的路径长度为3。
我们继续回归正题,前向星的存图和邻接矩阵的含义大大的不同。
前向星存图:
struct node{
int next,to;
int val;
}a[10010];
这个代码中,next 是 i 的兄弟,也可以理解为同一层的。
to 是 i 的终点,就是 i 的下一层。
val 是 i 的边权,就是代表从 i 到 i.to 的边权。
遍历代码:
int cnt,head[10010];
void add(int u,int v,int w){
a[++cnt].to = v;
a[cnt].w = w;
a[cnt].next = head[u];
head[u] = cnt;
}
head[i]第 i 个点的层数。
例题1
B3643 图的存储(洛谷)
题目描述:
给定一个n个顶点m条边的无向图。请以邻接矩阵和邻接表的形式输出这一张图。
输入格式:
第一行输入两个正整数n和m,表示图的顶点数和边数。
第二行开始,往后m行,每行输入两个以空格隔开的正整数u,v,表示 u,v 顶点之间有一条边直接相连。
输出格式:
首先输出n行n列的矩阵,以空格隔开每一行之间的数表示邻接矩阵。第i行第j列的数为1则表示顶点i,j之间有一条边直接相连;若为0则表示没有直接相连的边。
再往后输出
n 行。第i行首先先输出一个整数di ,表示这个顶点的度数,再按照从小到大的顺序,依次输出与顶点 i 直接相连的所有顶点。
输入输出样例:
输入
5 5
1 2
2 3
3 5
1 3
3 4
输出
0 1 1 0 0
1 0 1 0 0
1 1 0 1 1
0 0 1 0 0
0 0 1 0 0
2 2 3
2 1 3
4 1 2 4 5
1 3
1 3
附上代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,u,v,d[1010],a[1010][1010];
vector<int>g[1010];
int main(){
cin >> n >> m;
while(m--){
cin >> u >> v;
g[u].push_back(v); //建双向边,从u到v
g[v].push_back(u); //建双向边,从v到u
d[u]++; //统计度数
d[v]++; //两边都要
a[u][v] = 1; //邻接矩阵存图
a[v][u] = 1;
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cout << a[i][j] << ' ';
}
cout << endl;
} //输出*1
for(int i = 1; i <= n; i++){
cout << d[i] << ' ';
sort(g[i].begin(),g[i].end());
for(int j = 0; j < g[i].size(); j++){
cout << g[i][j] << ' ';
}
cout << endl;
} //输出*2
return 0;
}
这里对邻接表存图做一个简单的介绍。
邻接表使用vector存图,vector
连通图
连通图:就是所有点都连在一起,没有被单独存放的。
连通分量:在图中选出连通子图,就是连通分量。
强连通图:在有向图中,对于任意两个不相同的顶点,他们两个都有路径,则称强连通图。
强连通分量:在有向图中,对于任意一个子图符合上述条件,就是强连通分量。
有环图
一个图只要可以无限被走下去就是有环图(对于有向图而言)
例题2
P3916 图的遍历(洛谷)
题目描述:
给出N个点,M条边的有向图,对于每个点 v,求A(v),A(v) 表示从点 v 出发,能到达的编号最大的点。
输入格式:
第1行 2个整数 N,M 表示点数和边数。
接下来M行,每行2个整数Ui,Vi,表示边(Ui,Vi)。点用1,2,…,N编号。
输出格式:
一行N个整数A(1),A(2),…,A(N)。
输入输出样例:
输入:
4 3
1 2
2 4
4 3
输出:
4 4 3 4
附上代码:
#include<bits/stdc++.h>
const int N = 1e5 + 10;
using namespace std;
int n,m,a[N];
vector<int>E[N];
void dfs(int x,int k) {
if(a[x]) return;
a[x] = k;
for (int i = 0; i < E[x].size(); i++) dfs(E[x][i],k);
}
int main(){
scanf("%d%d",&n,&m);
while(m--){
int x,y;
scanf("%d%d",&x,&y);
E[y].push_back(x);
}
for (int i = n; i; i--) dfs(i,i);
for (int i = 1; i <= n; i++) printf("%d ",a[i]);
return 0;
}
束语
感谢大家对我C++结构体(STRUCT)的支持。
今天我们就讲到这里,拜拜!