图的存储
邻接矩阵
邻接矩阵适合存储稠密图。
在邻接矩阵 a a a 中,如果 a i , j a_{i,j} ai,j 为 ∞ \infty ∞,代表 i i i 与 j j j 之间没有边,否则 a i , j a_{i,j} ai,j 代表边 i → j i\rightarrow j i→j 的权值。
Example:
在邻接矩阵中这样存储:
∞ ∞ 2 ∞ ∞ ∞ ∞ ∞ ∞ 3 ∞ ∞ 2 ∞ ∞ 4 ∞ ∞ ∞ 3 ∞ ∞ 1 5 ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ \begin{array}{l} \infty & \infty & 2 & \infty & \infty & \infty \\ \infty & \infty & \infty & 3 & \infty & \infty \\ 2 & \infty & \infty & 4 & \infty & \infty \\ \infty & 3 & \infty & \infty & 1 & 5 \\ \infty & \infty & \infty & \infty & \infty & \infty \\ \infty & \infty & \infty & \infty & \infty & \infty \\ \end{array} ∞∞2∞∞∞∞∞∞3∞∞2∞∞∞∞∞∞34∞∞∞∞∞∞1∞∞∞∞∞5∞∞
代码实现:
const int N = 1000;
int g[N][N]; // 邻接矩阵
// me -> 起点 | e -> 终点 | w -> 权值
// 把 me -> e 这条路的权值设为 w
void add(int me, int e, int v) { g[st][ed] = v; }
邻接表
邻接表用于在内存不够,无法使用邻接矩阵时存储图。
邻接表使用四个列表来存储图,hd
和 nxt
实现链表存储每个点的出边,ed
存储每个边的终点,w
存储每个边的权值,用变量 idx
存储当前边的编号。
实现:
const int N = 10010, M = 100010;
int idx, hd[N], nxt[M], ed[M], w[M];
// N -> 点数 | M -> 边数
void add(int me, int e, int v) {
ver[++idx] = e; // 设置终点
w[idx] = v; // 设置权值
nxt[idx] = hd[me]; // 实现链表
hd[me] = idx;
}
Example:
输入:
1 3 2
3 1 2
3 4 4
4 2 3
4 5 1
4 6 5
2 4 3
每次链表的状态:
hd: 1 0 0 0 0 0 | nxt: 0 0 0 0 0 0 0
hd: 1 0 2 0 0 0 | nxt: 0 0 0 0 0 0 0
hd: 1 0 3 0 0 0 | nxt: 0 0 2 0 0 0 0
hd: 1 0 3 4 0 0 | nxt: 0 0 2 0 0 0 0
hd: 1 0 3 5 0 0 | nxt: 0 0 2 0 4 0 0
hd: 1 0 3 6 0 0 | nxt: 0 0 2 0 4 5 0
------------------------------------
hd: 1 7 3 6 0 0 | nxt: 0 0 2 0 4 5 0
注:hd
和 idx
中存储的都是边的编号。
寻找节点 4 4 4 的出边:
访问 hd[4]
为
6
6
6,代表有一条边
4
→
e
d
6
=
6
4\rightarrow ed_6=6
4→ed6=6;
访问 nxt[6]
为
5
5
5,代表有一条边
4
→
e
d
5
=
5
4\rightarrow ed_5=5
4→ed5=5;
访问 nxt[5]
为
4
4
4,代表有一条边
4
→
e
d
4
=
2
4\rightarrow ed_4=2
4→ed4=2;
访问 nxt[4]
为
0
0
0,代表没有其他出路。
代码如下:
int f; // 要寻找出路的节点
// 一直访问 nxt[i] 直到 i 为 0
for (int i = hd[f]; i; i = nxt[i])
printf("%d %d\n", f, ed[i]);
// 输出访问到的边的起点、终点编号。