我们就以下图为例:
根据建边的顺序,我们可以给这些边都创建一个序号,如:
1 2 <---1号
2 3 <---2号
3 4 <---3号
1 3 <---4号
4 1 <---5号
1 5 <---6号
4 5 <---7号
按照《算法竞赛》中给出的思路,我们可以写出链表:
1 -> 5 -> 3 -> 2
2 -> 3
3 -> 4
4 -> 5 -> 1
5
这样,我们可以从后往前地观察,维护一个head[i]
数组,表示输入边中的弧头为i
的最后一条边的编号,即:
head[1] = 6
head[2] = 2
head[3] = 3
head[4] = 7
head[5] = 0(即不存在弧头为5的边)
我们在存边的时候也可以顺便维护to[i]
数组,即第i
条边的弧尾。这个太简单了,就不说了。
最后我们维护next[i]
数组,我们设第i
条边的弧头为x
,则next[i]
表示在第i
条边之前最后一条弧头为x
的边的编号,即:
next[1] = 0
next[2] = 0
next[3] = 0
next[4] = 1
next[5] = 0
next[6] = 4
next[7] = 5
拿到这三个数组后,我们就可以实现加边操作。
void AddEdge (int u, int v, int x) {
to[++ cnt] = v;
w[cnt] = x;
next[cnt] = head[u];
head[u] = cnt;
}
当我们要枚举这个点的邻接点时,只需用for
循环遍历即可。
for (int i = head[x]; i; i = next[i]) {
//...
}
例题:邻接表存储带权的无向图
代码
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN = 1000 + 5;
int n, m, u, p, x, cnt, to[MAXN], head[MAXN], w[MAXN], next[MAXN];
struct node {
int x, w;
bool operator < (const node x) const {
if (w == x.w) {
return this->x < x.x;
} else {
return w < x.w;
}
}
};
vector <node> v;
void AddEdge (int u, int v, int x) {
to[++ cnt] = v;
w[cnt] = x;
next[cnt] = head[u];
head[u] = cnt;
}
int main () {
scanf ("%d %d", &n, &m);
for (int i = 1; i <= m; i ++) {
scanf ("%d %d %d", &u, &p, &x);
AddEdge (u, p, x);
AddEdge (p, u, x);
}
for (int i = 1; i <= n; i ++) {
v.clear();
for (int j = head[i]; j; j = next[j]) {
v.push_back((node) {to[j], w[j]});
}
sort (v.begin(), v.end());
for (int i = 0; i < v.size(); i ++) {
printf ("%d ", v[i].x);
}
printf ("\n");
}
return 0;
}