对于节点 i , j
邻接矩阵:
开二维数组 G[][] ,
int G[N][N];
用G[i][j]存储边的权值,如果是
无向图,G[i][j]=G[j][i]= i->j 或 j->i 的权值
有向图则令G[i][j]和G[j][i]分别为 i->j 和 j->i 各自的权值。
如果 i , j 之间并没有直连边,可以将G[i][j]和G[j][i]设置为某个特殊值,如 -1,0x3f3f3f3f 等,但是不建议设置为0x7fffffff或者0x7f7f7f7f,因为这有可能在计算中导致数据溢出。
优点:
编码简单,访问快捷高效
适合稠密图
缺点:
如果 i,j 之间有多条边则无法存储
对于稀疏图则严重浪费空间
无法应对节点数较多的图
邻接表:
为了应对邻接矩阵无法存储i,j间多条边的情况,可以建立邻接表
对于每一个节点i,用一个结构体存储他的所有直连边以及对应的权值
struct edge
{
int to,length;
edge (int TO,int LENGTH)
{
to=TO;
length=LENGTH;
}
};
再利用C++的vector存储每一个点的结构体,这样可以省下很多空间
vector <edge> e[N];
初始化:
将以 i 为起点的边的数据写入 e[i] :
e[i].push_back(edge(j,len));
访问 i 的所有直连边时,用一个 for 循环遍历 e[i] 即可
邻接表的缺点是想要找到他的某个邻居就需要遍历他的所有邻居,效率不如邻接矩阵,但是一般邻居不会太多,影响不大
链式前向星:
思路:
有时候边数太多,邻接表也无法应对,这时候我们就需要想办法节省空间,这一方法就是按顺序将所有的边存储起来
这么存储会遇到一个问题,如何找到某个节点的所有直连边?
不难想到,如果对节于点 i 的某个已知的邻居 j ,他都能告诉我们在他前面出现的节点i的另外一个邻居的存储编号,即nex,那么以此类推,我们就可以找到 j 以及在 j 前面出现的 i 的所有邻居。因此,只要保证 j 是 i 的最晚出现的邻居,就可以遍历 i 的所有邻居了。由此我们不难联想到单链表的思想,只需用一个数组记录某个节点最后一个邻居的存储编号(即表头)head[i],便可以按照他们出现的顺序的逆序遍历
如图,记录节点1最晚出现的邻居6被存储在6号,就从6号开始遍历
6号记录上一个邻居存储在3号,于是进入到三号,再到1号,最后遍历到0号,结束遍历
于是重点便是:
每当加入一条新边,就要及时更新该边起点 i 的“最晚出现的邻居的位置”,也即单链表中加入一个新元素的情况:
新加入的边的nex就是未更新的head[i],新的head[i]就是当前的存储编号
如图,head[1] 变成了8,8号的nex就是更新之前的head[1]==6,这样便能保证从8号开始沿着nex遍历到0号可以不剩余地遍历以节点1为起点的所有边
于是便有以下代码
int cnt,head[N];
struct edge
{
int to,length,nex;
}e[N];
void add(int from,int to,int len)
{
e[++cnt].to=to;
e[cnt].length=len;
e[cnt].nex=head[from];
head[from]=cnt;
}
其中结构体e用于按顺序存储所有的边
head[i] 记录以i 为起点的边的最后一条存储在几号