一,前言
说实话,看到时候没有看懂,自己模拟了一下 边是怎么 加上去的,才搞明白。
想明白之后,才知道被这些 变量名 给忽悠了,我不知道取名的人是怎么想的,
但我觉的有些变量名并不恰当,直接把我带到沟里去了,所以这里 我写的代码源自
自己的习惯,勿怪!๑乛◡乛๑ (●´∀`●)
二,引子
想要通过 一组有序的点来记录 一条路径 的话,你只需要 两个变量,
一个记录当前点的编号,一个记录前一个点的编号(在这里我有详细讲到 https://www.cnblogs.com/asdfknjhu/p/12499112.html)
但如果不是一条路,而是一张图,又有什么不同呢?
在 一条路中 ,一个点的前一个点是唯一的,而如果是图的话,就会出现 一点 映射 多点 的情况,
这样你就不知道要设 几个变量 来存 前一个点的编号了。
所以,重点来了(..•˘_˘•..) ,要存图,你需要 存放的是一个边的所有信息。于是,你需要构造一个结构体数组,其中每个结构体代表一条边。
三,边
那么一条边你需要存什么信息呢?
答案是:起点,终点(知道了起点和终点就可以确认方向了),权值
那么问题来了,知道了终点就可以找到,以此终点为起点的边,但是那些相同起点的边你怎么根据其中一条找到剩下的边(即怎么把相同起点的边连接起来)
所以,重点又来了,这一步可是整个链式前向星的精髓 (..•˘_˘•..)
因为起点无法直接使用,于是我们 取消 结构体中需要的起点变量,另设两个变量
一个是在结构体中 int :我称之为,pre : 即与本边 同一起点的前一条边的 结构体编号,
一个是 int 数组:last: :last[i] 里面存的是 表示以第 i 个点为起点 的所有边中 最后一条边的 结构体编号
为什么是 以第 i 个点为起点, 可以看添加边时,last 数组的赋值,下标用的是 起点的编号。
没错,我说名字误导我的就是这两个变量,原名为 next, head/first,我也是醉了,跟我理解的完全相反 ٩͡[๏̯͡๏]
有了这两个变量,我们就可以根据,最后一条边,往前遍历所有起点相同的边了。
四,边集
即结构体数组,我们根据边添加的顺序对边进行编号,即把某条边放进哪一个结构体,就是按顺序来,先来的一号,后来末尾。
通过 to 和 pre 链接边。
to: 向下搜索
pre:横向搜索
(•‾̑⌣‾̑•)✧˖°
由于结构体数组一开始肯定要开很大,所以为了记录边的数量且为了便于添加边
我们 还需要最后一个变量 int tot.
五,总结
综上,我们需要 一个结构体数组 e,
里面的变量有 to : 终点
w; 权值
pre;即与本边 同一起点的前一条边的 结构体编号,
还要一个数组 last, 一个变量 tot
六,具体实现
初始化:
struct Node { int pre; int to; int w; }e[N]; int last[N],vis[N]; int tot=0; // 没有边 memset(last, -1, sizeof(last)); // 初始化为 -1 ,因为结构体数组要从 0 开始
加边函数
void add(int from, int to , int w) //按顺序为 起点,终点,权值 {
tot++; e[tot].to = to; e[tot].w = w; e[tot].pre = last[from]; // 给新加的边,赋上 前一个相同起点的边的编号 last[from] = tot; // 更新 lasi 数组,新加入的边 才是这些起点相同的边中 最后一个 }
求以第 x 个点为起点 的所有边的权值
以 以该点为起点 的最后一条边开始,往前遍历所有起点相同的边,当 pre == -1 时,表示前面没有变了。
int sum = 0; for (int i = last[x];~i;i=e[i].pre) { sum += e[i].w; }
遍历所有边: 调用时 dfs(1); //从 第一个点 开始
int ci = 0; void dfs(int s) // 所找到的顺序与添加的顺序不一样 { vis[s] = 1; for (int i = last[s]; ~i; i = e[i].pre) // 同一个 for 循环起点都相同 { printf("第 %d 条边: 起点:%d 终点:%d 权值:%d ", ++ci, s, e[i].to, e[i].w); if (vis[e[i].to] == 0) // 以该终点为起点,向下搜索 { dfs(e[i].to); } } }
代码总结:
题目:来源 http://www.fjutacm.com/Contest.jsp?cid=863#P12
我多加了一个功能。遍历图
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<string.h> #define N (int)2e6+5 struct Node { int pre; int to; int w; }e[N]; int tot = 0; // 没有边 int last[N], vis[N]; void add(int from, int to, int w) //按顺序为 起点,终点,权值 { tot++; e[tot].to = to; e[tot].w = w; e[tot].pre = last[from]; // 给新加的边,赋上 前一个相同起点的边的编号 last[from] = tot; // 更新 lasi 数组,新加入的边 才是这些起点相同的边中 最后一个 } int ci = 0; void dfs(int s) // 所找到的顺序与添加的顺序不一样 { vis[s] = 1; for (int i = last[s]; ~i; i = e[i].pre) // 同一个 for 循环起点都相同 { printf("第 %d 条边: 起点:%d 终点:%d 权值:%d ", ++ci, s, e[i].to, e[i].w); if (vis[e[i].to] == 0) // 以该终点为起点,向下搜索 { dfs(e[i].to); //vis[s] = 0; // 这句 不能有,不然会重复找, } } } int main(void) { int n, m, q; while (scanf("%d%d%d", &n, &m, &q) != EOF) { memset(last, -1, sizeof(last)); for (int i = 1; i <= m; i++) { int x, y, z; scanf("%d%d%d", &x, &y, &z); add(x, y, z); if (x != y) add(y, x, z); } // 询问 以第 x 个点为起点 的所有边的权值 while (q--) { int x; scanf("%d", &x); if (last[x] == -1) printf("第 %d 个点为起点 的所有边的权值:NULL ", x); else { int sum = 0; for (int i = last[x]; ~i; i = e[i].pre) { sum += e[i].w; } printf("第 %d 个点为起点 的所有边的权值:%d ", x, sum); } } puts(""); // 遍历 dfs(1); //从 第一个点 开始 } return 0; }
========== ========= ========= ======= ===== ===== === == =
渡荆门送别 唐 李白
渡远荆门外,来从楚国游。
山随平野尽,江入大荒流。
月下飞天镜,云生结海楼。
仍怜故乡水,万里送行舟。