图的存储结构

邻接矩阵

对于有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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值