一、邻接矩阵:
内存占用较高,仅适用于0<=n<=1000的图,在简单的图算法题目中较为常见。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=0x7fffffff;
const int maxn=1005;
int n,_num;
int a[maxn][maxn];//储存图
int main(void)
{
while(scanf("%d%d",&n,&_num)!=EOF)
{
//初始化
memset(a,0,sizeof(a));
memset(a,-1,sizeof(a)); //a[i][j]=-1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[i][j]=inf;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[i][j]=-inf;
int pre,_next,vi;
//有向图建立
for(int i=1;i<=_num;i++)
{
scanf("%d%d%d",&pre,&_next,&vi);
a[pre][_next]=vi;
}
//无向图建立
for(int i=1;i<=_num;i++)
{
scanf("%d%d%d",&pre,&_next,&vi);
a[pre][_next]=vi;
a[_next][pre]=vi;
}
}
return 0;
}
二、邻接链表:
使用vector模拟邻接链表,较邻接矩阵更加节省内存空间,内存利用率高,简单好写。
是处理图或树算法题的最经常用的存图方式。能处理1e5数据。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int inf=0x7fffffff;
const int maxn=100005;
int n,m;
//节点及边权值
struct node
{
int x;//节点
int vi;//边权值
node(){}
node(int a,int b)
{
x=a;
vi=b;
}
};
//邻接表
vector<node>vc[maxn];
//有向图入度
int _num[maxn];
//初始化
void init(int n)
{
for(int i=1;i<=n;i++)
{
vc[i].clear();
_num[i]=0;
}
return ;
}
int main(void)
{
while(scanf("%d%d",&n,&m)!=EOF)
{
//初始化
init(n);
int pre,_next,vi;
//有向图建表
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&pre,&_next,&vi);
vc[pre].push_back(node(_next,vi));
_num[_next]++;
}
//无向图建表
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&pre,&_next,&vi);
vc[pre].push_back(node(_next,vi));
vc[_next].push_back(node(pre,vi));
}
}
return 0;
}
三、链式前向星:
储存稀疏图,边集,时间效率优于vector。
对于链式前向星,可以由结构体+数组实现,也可以完全由数组实现。
head数组的大小为图或树的节点数的个数。
对于树来说,若有向, 其余各数组大小均应该开到节点数大小;
若无向, 其余各数组大小均应该开到节点数的二倍大小。
对于图来说,若有向, 其余各数组大小均应该开到边数量大小;
若无向, 其余各数组大小均应该开到边数量二倍大小。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<stack>
#define ll long long
using namespace std;
const int maxn=100008;
const int inf=0x7fffffff;
const int mod=1000000007;
int head[maxn],nt[maxn],ver[maxn],edge[maxn];
//若建立无向图则应采取以下写法
//int head[maxn],nt[2*maxn],ver[2*maxn],edge[2*maxn];
int tot=0;
// int tot=1;
void add(int x,int y,int z)
{
//head与nt数组中保存的是ver数组的下标
//相当于指针,其中0(-1)表示指向空
//ver数组储存的是每条边的终点,是真实的图数据
//edge保存的是每一条边的权值
ver[++tot]=y,edge[tot]=z;//真实数据
nt[tot]=head[x],head[x]=tot;//在表头x处插入
return ;
}
//访问从x出发的所有边
void fi(int x)
{
for(int i=head[x];i;i=nt[i])
{
int y=ver[i],z=edge[i];
//找到一条x——>y的有向边,权值为z
}
return ;
}
int main(void)
{
//初始化为0,或者-1,代表为空的数据
memset(head,0,sizeof(head));
memset(head,-1,sizeof(head));
int n,m;//n个节点,m条边
int pre,_next,vi;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&pre,&_next,&vi);
//有向边
add(pre,_next,vi);
//无向边
add(_next,pre,vi);
}
//查询每一条边出发能到达的节点
for(int i=1;i<=n;i++)
{
fi(i);
}
return 0;
}
以后的写代码的时候尽量适应用第三种建图方式。