图论学习笔记——图的储存结构

1、二维数组邻接矩阵储存

这是一种以点来储存图的方式

具体操作就是建立一个整形二维数组G,G[i][j]表示从i点到j点的边的权值(有向),无向图可以灵活变通运用,如果图中只存在边通不通的情况可以建立bool类型的数组节约内存。

2、数组模拟邻接表储存(链式前向星)

①、前向星

在学习链式前向星之前,我们首先要明白什么是前向星,也就是链式前向星的前身。

相对于用二维数组储存邻接矩阵以两点之间是否相连(如果相连权值是多少)的储存方式,在一些比较稀疏的图(一个边数远远少于完全图的图),以储存边的方式来储存图的效率明显比以储存点的方式来储存图高得多。

先将边读入并存储在连续的数组中,然后按照边的起点进行排序,这样数组中起点相等的边就能够在数组中进行连续访问了。缺点是需要在所有边都读入完毕的情况下对所有边进行一次排序,带来了时间开销,实用性也较差,只适合离线算法。

下图转自https://www.cnblogs.com/Renyi-Fan/p/7508063.html,该图解释了前向星的具体用法。

 

②链式前向星

链式前向星是针对前向星的优化,它解决了排序问题,使效率得到提高。

我们首先要建立一个结构体边(edge),包含四个对象,u、v、w、next,分别对应这条边的起始点、终止点、权值、和与这条边起点相同的下一条(其实是数据读入顺序的上一条)边。ps:至于为啥说它是上一条边在下文解释,我刚开始的时候也蒙逼了好久。

咳,突然发现不懂怎么表达了。。。以下内容转自https://www.cnblogs.com/Renyi-Fan/p/7508063.html

红字部分为笔者注释。

 具体的,我们需要一个边的结构体数组 edge[MAXM],MAXM表示边的总数,所有边都存储在这个结构体数组中,并且用head[i]来指向 i 结点的第一条边。

       边的结构体声明如下:

    struct EDGE {
                    int u, v, w, next;
        EDGE() {}
        EDGE(int _u, int _v, int _w, int _next) {
            u = _u, v = _v, w = _w, next = _next;
        }
    }edge[MAXM];

       初始化所有的head[i] = INF,当前边总数 edgeCount = 0

       每读入一条边,调用addEdge(u, v, w),具体函数的实现如下:

    void addEdge(int u, int v, int w) {
        edge[ edgeCount ] = EDGE(u, v, w, head[u]);
        head[u] = edgeCount ++;
    }

       这个函数的含义是每加入一条边(u, v),就在原有的链表结构的首部插入这条边,使得每次插入的时间复杂度为O(1),所以链表的边的顺序和读入顺序正好是逆序的。这种结构在无论是稠密的还是稀疏的图上都有非常好的表现,空间上没有浪费,时间上也是最小开销。这段划重点,咳。

       调用的时候只要通过head[i]就能访问到由 i 出发的第一条边的编号,通过编号到edge数组进行索引可以得到边的具体信息,然后根据这条边的next域可以得到第二条边的编号,以此类推,直到next域为INF(这里的INF即head数组初始化的那个值,一般取-1即可)。

 

 

但是我们也不能只白嫖是吧,干货还是要有一些的,咳。

贴上实现代码模板:

#incldue<iostream>
#incldue<cstring>
using namespace std;
const int MAX=100;
struct Edge
{
  int next;//the next(in fact is the last) edge whose from_node the same as itself;
  int to;//the next node;
  int w;//data;
}edge[MAX];
int head[MAX];//tail you which dege is the first(in fact is the lastest) from_node in;
int cnt=1;//count the member of dege;

void edge_add(int from, int to, int w)
{
  edge[cnt].w = w;
  edge[cnt].to = to;
  edge[cnt].next = head[from];
  head[from] = cnt;
  ++cnt;
}

int main()
{
  memset(head, 0, sizeof(head));//use zero to as the sign of end;
  ...
  add();
  ...
  //how to traversal?
    for(int i=head[1]; i!=0; i=edge[i].next)
      //if i==0, is sign all the edge were visited;
  return 0;
}

上面的代码均采用英文注释(欢迎英语大佬提出语法和用词的错误QAQ),因为用中文在一些辣鸡编译器中会乱码。。。英语不好的同学(其实我也是个英语渣)建议简单的学习一下,毕竟要走编程这条路英语还是要掌握的。


在前文的标题中我们提到这是用数组模拟邻接储存表的,而邻接储存表本来是要采用链表也就是结构体指针来实现的。。。但是结构体指针的抽象程度呢又比上文提到的东西稍微大那么一点点。。。所以大多数情况我们只需要使用数组模拟即可(也就是那个head的数组),如果感兴趣的话可以去瞅一瞅结构体指针然后试着实现一下。。。笔者自己尝试过但是不知道对不对,如果方便的话可以私信笔者私下交流一下学习经验。咳,那段丑恶的代码我就不贴了。

 

以上均属个人现阶段的观点,本人是蒟蒻一枚,水平有限,如有不足请各位神犇指出。

版权申明:欢迎转载,转载请注明出处!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值