邻接表(链式向前星)
链式向前星:用来储存边的信息的以中方法。优点:速度快,节省空间,很常用
如果我们用 Vector 来建边,相当于开的是一个动态的二维数组,较数组模拟来说比较慢。
有时候还卡空间(被某一道题卡崩的YMF学长:我再用Vector建边我是🐶)。
我先把基本代码贴出来。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
struct node {
int e; //边的终点
int c; //边权
int p; //同一个起点的上一条边的编号
} Edge[maxn << 1];
///如果是有向边只需要maxn,无向边需要双倍
///这个是特别容易忘记的地方
int head[maxn], cnt;
/// head[i]数组记录以i为起点的边的最后一个编号
/// cnt记录边的编号
void add_edge(int s, int e, int c) { ///边的起点,终点和边权
Edge[++cnt] = node{e, c, head[s]};
head[s] = cnt;
}
///记得初始化
void init() {
cnt = 0;
memset(head, -1, sizeof(head)); ///没有相关的边,初始化为-1
}
int main() {
init();
int n, m, s, e, c;
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; i++) {
scanf("%d %d %d", &s, &e, &c);
add_edge(s, e, c);///建立有向边
///如果需要建立无向边,需要再反向建边
//add_edge(e,s,c);
}
///访问信息: s为起点的边
for (int i = head[s]; i != -1; i = Edge[i].p) { ///i!=-1 可以写成 ~i
e = Edge[i].e, c = Edge[i].c;
///s 为起点,e为终点,c为边权
}
return 0;
}
我们利用一个结构体数组将边的信息线性的就储存下来了。代码量较其他两种比较多,但是效率和空间都节省了。
如果我们输入7条边(以单向边为例子):
起点 | 终点 | 边权 |
1 | 2 | 1 |
2 | 3 | 2 |
3 | 4 | 3 |
4 | 1 | 4 |
6 | 7 | 0 |
1 | 3 | 3 |
1 | 4 | 4 |
按照我们上面的代码来模拟这个过程
加入第一条边前 :head[1]=-1,cnt=0;加完过后 head[1]=1,cnt=1,Edge[1]={2,1,-1}。
加入第二条边前 :head[2]=-1,cnt=1;加完过后 head[2]=2,cnt=2,Edge[2]={3,2,-1}。
加入第三条边前 :head[3]=-1,cnt=2;加完过后 head[3]=3,cnt=3,Edge[3]={4,3,-1}。
加入第四条边前 :head[4]=-1,cnt=3;加完过后 head[4]=4,cnt=4,Edge[4]={1,4,-1}。
加入第五条边前 :head[6]=-1,cnt=4;加完过后 head[6]=5,cnt=5,Edge[5]={4,0,-1}。
加入第六条边前 :head[1]=1,cnt=5;加完过后 head[1]=6,cnt=6,Edge[6]={3,3,1}。
加入第七条边前 :head[1]=6,cnt=6;加完过后 head[1]=7,cnt=7,Edge[7]={4,4,6}。
起点 | 1 | 2 | 3 | 4 | 6 | 1 | 1 |
终点 | 2 | 3 | 4 | 1 | 7 | 3 | 4 |
边权 | 1 | 2 | 3 | 4 | 0 | 3 | 4 |
编号 i | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
Edge[ i ].p | -1 | -1 | -1 | -1 | -1 | 1 | 6 |
最终的head数组
head[1] | head[2] | head[3] | head[4] | head[5] | head[6] |
7 | 2 | 3 | 4 | -1 | 5 |