邻接矩阵
对于有n个点的图,用一个n.n的矩阵存储点的信息,map[i][j]的值表示点vi 到vj的距离。邻接矩阵需要初始化,map[i][i] =0,map[i][j]=INF(i!=j),对于每组读入的vi,vj,w(边的起点,边的终点,权值),赋值map[i][j]=w即可
时间复杂度:o(n^2) (初始化要o(n^2),建图要o(m))
空间复杂度:o(n^2) (取决于点的个数)
优点:实现简单直观,并且可以直接查询点vi和vj间是否有边,如果有权值是多少
缺点:遍历效率较低,且不能存储重边,初始化效率低,大图的空间开销大, 对于稀疏图的空间利用率不高
前向星
读入每条边的信息,将边存放在数组中,把数组中的边按照起点顺序排序,如果起点相同,对于相同起点的边按终点排序,如果仍有相同,按权值排序。通常会有一个数组存储起点为vi的第一条边的位置
所需的数据结构:
int head[madxn]; //存储起点为vi的第一条边的位置
struct NODE
{
int from;//起点
int to;//终点
int w;//权值
} ;
NODE edge[maxn];
信息存储的主要代码:
比较函数:
bool cmp(NODE a,NODE b)
{
if(a.from==b.from&&a.to==b.to)
return a.w<b.w;
if(a.from==b.from)
return a.to<b.to;
return a.from<b.from;
}
读入数据:
cin>>n>>m;
for(int i=0;i<m;i++)
cin>>edge[i].from>>edge[i].to>>edge[i].w;
sort(edge,edge+m,cmp);
memset(head,-1,sizeof(head));
head[edge[0].from]=0;
for(int i=1;i<m;i++)
if(edge[i].from!=edge[i-1].from)
head[edge[i].from]=i;
遍历代码:
for(int i=1;i<=n;i++)
for(int k=head[i];edge[k].from==i&&k<m;k++)
cout<<edge[i].from<<" "<<edge[i].to<<" "<<edge[i].w<<endl;
时间复杂度:O(mlogm)
空间复杂度:O(m+n)
优点:可以应对点非常多的情况,可以存储重边
缺点:不能直接判断任意两个顶点(vi和vj)之间是否有边,而且排序要浪费一定时间
注意:邻接矩阵的值和边的输入顺序无关
邻接表
对于图G中每个顶点vi,把所有邻接于vi的顶点vj链成一个单链表
邻接表的三种实现方式:动态建表,使用STL中的vector模拟链表,静态建表
- 动态建表
时间复杂度:o(m)
空间复杂度:o(m)
优点:不会浪费多余的空间
缺点:内存不便于释放,且相较一次性内存的申请耗时较长,判断任意两个顶点之间是否有边效率低
动态建表的数据结构:
struct EdgeNode //邻接表节点
{
int to; //终点
int w; //权值
EdgeNode *next; //指向下一条边的指针
};
struct VNode //起点表节点
{
int from; //起点
EdgeNode *first; //邻接表头指针
};
VNode Adijlist[maxn]; //整个图的邻接表
信息存储主要代码:
cin>>i>>j>>w;
EdgeNode *p=new EdgeNode();
p->to=j;
p->w=w;
p->next=Adjlist[i].first;
Adjlist[i].first=p;
遍历代码:
for(int i=1;i<n;i++)
for(EdgeNode *k=Adjlist[i].firsr;k!=NULL;k=k->next)
cout<<i<<"<<k->to<<"<<k->w<<endl;
- STL中的vector模拟链表实现
优点:代码量较少,不易犯错误,内存的申请和释放都不需要自己处理
struct EdgeNode
{
int to;
int w;
};
vector < node > map[maxn];
信息存储主要代码:
EdgeNode e;
cin>>i>>j>>w;
e.to=j;
e.w=w;
map[i].push_back(e);
遍历代码:
for(int i=1;i<=n;i++)
{
for(vector<NODE>::iterator k=map[i].begin();k!=map[i].end;k++)
{
NODE t=*k;
cout<<i<' '<< t.to <<' '<<t.w<<endl;
}
}
- 静态建表(链式前向星)
优点:额外空间使用少,是目前建图和遍历效率最高的存储方式,可以存储重边,还可以应付点和边数量都很大的情况,没有内存管理,更安全
缺点:不能直接用起点终点确定边
时间复杂度:o(m)
空间复杂度:o(m+n)
数据结构:
int head[n];//存储描述点vi边信息的链的起点在Edges数组的位置
struct EdgeNode
{
int to;
int w;
int next;
};
EdgeNode Edges[m];
信息存储主要代码:
cin>>i>>j>>w;
Edges[k].to=y;
Edges[k].w=w;
Edges[k].next=head[i];
head[i]=k;
遍历代码:
for(int i=1;i<=n;i++)
{
for(int k=head[i];k!=-1;k=edge[k].next)
cout<<i<<' '<<edge[k].to<<' '<<edge[k].w<<endl;
}