图论基础——建图 笔记与思路整理

今天上课的时候Zimo_Lee说他把如何建图给忘了……在嘲笑他的同时写一篇随笔,也整理一下自己的思路。

先贴一张VisuAlgo(真香)上的图:

这是一张有向有权图,讲解就围绕这个开始。如果是无向图,就每次建边时建一条反向边即可。


一、邻接矩阵

原理很简单,如果graph[i][j]存在数据x,则代表有一条边从i指向j,权为x。代码应该也不用多解释:

graph[100][100];
void add_edge(int from, int to, int value){
    graph[from][to] = value;
}

邻接矩阵很好理解,代码很简单,而且在有时遍历会很方便。缺点也很明显,当图比较稀疏的时候需要很多不必要的空间,会造成很大的空间浪费。

二、邻接表

同一个点发出的边存在一个数组里。相对来说会比邻接矩阵省空间,也可以很方便地遍历同一个点发出的所有边。

由于动态数组需要,我习惯使用vector存储每个结点。

graph[i][cnt] = (j, x)表示第i个点发出的第cnt条边指向j,权为x。代码:

struct edge{
    int to, val;
};
vector<edge> graph[100];
void add_edge(int from, int to, int value){
    graph[from].push_back((edge){to, value});
}

三、邻接链表

整个表中的每一个元素代表一条边,边的起始、结束与权值均存储在这个元素中(一般用结构体)。经过优化后的邻接链表用一个first[]数组存储每个点中的第一条边,结构体中增加一项next存储同一起始结点的下一条边。这样,想要遍历所有从n号结点起始的边,只要使用

for(int i=first[n]; i; i=graph[i].next)  

即可,时间上与邻接表基本一致。

在代码实现上,优化只需要每次建边时把之前的first[from]设置为当前边的next,first[from]改为当前边的序号。代码:

struct edge{
    int to, val, next;
};
edge graph[10000];
int first[100], edge_cnt = 0;
void add_edge(int from, int to, int value){
    graph[++edge_cnt] = (edge){to, value, first[from]};
    first[from] = edge_cnt;
}

这三种是c++建图的最常用方式,要根据实际情况具体分析选择合适的存储结构。并不是越复杂的结构就越好用

posted @ 2019-09-05 20:42  mzWyt  阅读(491)  评论(1编辑  收藏  举报