图的储存
在信息学竞赛中,我们经常会遇到关于图的题目,我们会经常用到dfs,bfs,最短路等的解题方法。
但是在对图进行操作之前,我们要先将图储存起来,本文将介绍两种最实用基础的建图方法。
先给出一个有向图G1:
和一个无向图G2:
圆圈代表点
箭头和直线代表边
红色数字代表权值
邻接矩阵
邻接矩阵是建图的最基础,最简单的方法。
工作原理:
设一个二维数组g[i][j]
- 对于有向图:g[i][j]表示从点i到点j有一条长度为g[i][j]的单向边
- 对于无向图:g[i][j]表示点i与点j之间有一条长度为g[i][j]的双向边
在一个图中,若两个点没有边,可以表示为他们之间的距离无限大 ∞ ∞ ∞
邻接矩阵示意图:
G1:
i/j | 1 | 2 | 3 | 4 |
---|---|---|---|---|
1 | ∞ ∞ ∞ | 3 3 3 | ∞ ∞ ∞ | ∞ ∞ ∞ |
2 | ∞ ∞ ∞ | ∞ ∞ ∞ | ∞ ∞ ∞ | 5 5 5 |
3 | 2 2 2 | 4 4 4 | 3 3 3 | ∞ ∞ ∞ |
4 | ∞ ∞ ∞ | ∞ ∞ ∞ | 2 2 2 | ∞ ∞ ∞ |
G2:
i/j | 1 | 2 | 3 | 4 |
---|---|---|---|---|
1 | ∞ ∞ ∞ | 2 2 2 | 4 4 4 | ∞ ∞ ∞ |
2 | 2 2 2 | ∞ ∞ ∞ | 3 3 3 | 1 1 1 |
3 | 4 4 4 | 3 3 3 | ∞ ∞ ∞ | 3 3 3 |
4 | ∞ ∞ ∞ | 1 1 1 | 3 3 3 | ∞ ∞ ∞ |
实现代码:
#include<iostream>
#define inf 99999999//无限大
using namespace std;
int g[10][10];
int n,x,y,z;//x:起点 y:终点 z:权值
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
g[i][j]=inf;//初始化
for(int i=1;i<=n;i++)
{
cin>>x>>y>>z;
g[x][y]=z;//连边
/*
无向图:
g[x][y]=z;
g[y][x]=z;
*/
}
//遍历
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(g[i][j]!=inf)
cout<<"The distance of from "<<i<<" to "<<j<<" is "<<g[i][j]<<endl;
else
cout<<"There is no edge from "<<i<<" to "<<j<<endl;
}
return 0;
}
邻接表
邻接表是一种链式储存结构,所占用的空间比邻接矩阵小,可以方便快速遍历一个点的所有出边。
工作原理:
- 设一个head数组储存第i个节点的第一条边
- 创建结构体edge,内含next变量,储存下一条边;to变量,表示从i到to有一条边;val变量,储存本边权值
- 编写加边函数add_edge(int from,int to,int dis)
- 遍历从k=head[i]开始,下一条边是e[k].next
对于一个无向图,可以执行加边函数两次,其中第一次作为from参数的变量第二次作为to参数,第一次作为to参数的变量第二次作为from参数。
邻接表示意图:
G1:
G2:
实现代码:
#include<iostream>
using namespace std;
int n,x,y,z;//x:起点 y:终点 z:权值
int head[15],sum;
struct edge{
int next;
int to;
int val;
}e[15];
void add_edge(int from,int to,int dis)
{
sum++;
e[sum].next=head[from];//下一条边为当前i点的第一条出边
e[sum].to=to;
e[sum].val=dis;
head[from]=sum;//将现在的边作为i点的第一条出边
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>x>>y>>z;
add_edge(x,y,z);
//add_edge(y,x,z);无向图
}
//遍历
for(int i=1;i<=n;i++)
for(int k=head[i];k!=0;k=e[k].next)
cout<<"The distance of from "<<i<<" to "<<e[k].to<<" is "<<e[k].val<<endl;
}