图论——三种存图方式

  今年暑假入坑了ACM,可以说开始的很晚了。平时看到各位大佬们的博客都写的很是精彩了,也想通过写博客来督促一下自己,总结一下自己的每天所学和一些感受吧,希望能一直坚持下去,这条路注定不会好走,那就坦然去接受一场场爆零,从零开始慢慢积累吧。(刚开始入坑,写的内容可能会有很多很多的问题,希望大家多多指出,一起进步。)

  本来是最近在准备学习网络流,但是发现,基础差到模板里建图就有些不会,于是先来学学习如果存图,大概有三种这样的方式:

一、邻接矩阵存图

   顾名思义就是用矩阵来记录一个图,矩阵中第 i 行第 j 列的值就表示顶点 i 到顶点 j 的权值。

   

#include<bits/stdc++.h>
const int V = 1000;  //最大顶点数
int mat[maxn][maxn];//开设二维数组来存矩阵

int main()
{
    int i,j,w;
    memset(mat, 0, sizeof(mat));  //初始化操作假设权值为0表示没有该边
    scanf("%d%d%d",&i,&j,&w);//输入边的信息:起点、终点、权重
    
    mat[i][j] = w;// //增加顶点i到顶点j的权值为w的边
    mat[i][j] = 0;//删除边
    printf("%d",mat[i][j]);//查询边
}

  优点:容易理解,如果矩阵已经确定,可在O(1)的时间复杂度实现添加,修改,删除的操作

       缺点:空间复杂度过高,并且极为致命,一旦顶点数过多,需要利用空间就很大,而当图很稀疏时候,就会造成内存的浪费

二、邻接表

   邻接表存储,又叫链式存储。这里主要说一下用数组的方式来模拟建立邻接表,特别是在网络流应用中,要建立好多好多数组,分别存取边的起点、终点、特别是还要存权重的数组域。具体解释看代码:

       

#include<bits/stdc++.h>
#define maxn 1000001
#define INF 119260817
using namespace std;
int  cnt,cost[maxn],from[maxn],to[maxn],Next[maxn], head[maxn];
int n,m;
void add(int x,int y,int z)//建边
{   
    ++cnt;
    cost[cnt]=z;//权重
    from[cnt]=x;起点
    to[cnt]=y;//汇点
    Next[cnt]=head[x];//存边表,存储上个边的号head[x],形成链表
    head[x]=cnt;//记录每个起点的相连的一个点,就是所在链表的起点
}
int main()
{
    cnt=1;
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);//输入n个顶点,m条边
    for(int i=1;i<=m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);//起点、终点、权重
        add(x,y,z);add(y,x,0);      //刚开始要建反向边,容量是0
    }
}

  由代码可知:此方法时间、空间复杂度都是O(m),遍历一条链表时复杂度也为O(m),远小于邻接矩阵中的O(n2)。

三、链式向前星

     感觉和邻接表存储有点相似,通过结构体数组来存储边的终点、上一个边、权重(因为一个结点同一起点所以不需要存起点了),再加一个head数组,head数组来存边的序号。

   

const int maxn = 10005;   //点的最大个数
int head[maxn], cnt=0;//head数组用来表示以i为起点的一条边存储的位置
                                  //cnt用来记录序号

struct Edge
{
    int to; //此边的终点
    int w; //此边的权重 
    int next; //同一起点的上一条边的储存位置
}Edge edge[maxn];

void add(int u,int v,int w)  //初始化加边
{
    edge[cnt].w = w;//记录边的权重
    edge[cnt].to = v;//记录边的终点
    edge[cnt].next = head[u];//存储同一起点的上一条边的位置
    head[u] = cnt++;//给边赋予序号
}

int main()  //遍历所有链表
{
    for(int i=0; i<=n; i++)
        for(int j=head[i]; j!=-1; j=edge[j].next)
             {......}//具体操作省略
}

      与上面邻接矩阵相比,最大缺点就是,一开始不容易理解了,需要先看下数据结构链表的东西,这个方法很节省内存,效率高。

      这就是三种存图的方式,学会了这三种就可以进一步学习图论的一些算法了。

      要一直坚持下去哦!

   

原文地址:https://www.cnblogs.com/dreamxy/p/11251628.html