zoukankan      html  css  js  c++  java
  • 图论——三种存图方式

      今年暑假入坑了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)
                 {......}//具体操作省略
    }

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

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

          要一直坚持下去哦!

       

  • 相关阅读:
    LeetCode18. 四数之和
    15. 三数之和
    LeetCode202. 快乐数
    LeetCode1. 两数之和
    LeetCode349. 两个数组的交集
    LeetCode242. 有效的字母异位词
    VSCode运行时弹出powershell
    关于cin, cin.get(), getchar(),getline()的字符问题
    剑指 Offer 27. 二叉树的镜像
    BFS zoj 1649
  • 原文地址:https://www.cnblogs.com/dreamxy/p/11251628.html
Copyright © 2011-2022 走看看