什么是链式前向星
链式前向星(Chained Forward Star)是一种用于表示稀疏图的数据结构。它主要用于解决图论中的一些算法问题,如最短路径、最小生成树等。
链式前向星通过两个数组来表示图的边和顶点信息:
- 边数组(Edge Array):存储了每条边的相关信息,如起点、终点、权重等。
- 下一个边数组(Next Edge Array):存储了每个顶点的第一条出发边的索引值,通过这个数组可以找到下一条以同一起点的边。
链式前向星的优势在于它能够高效地处理稀疏图,节省了存储空间,并且能够在常数时间内访问某个顶点的所有出发边
在我看来:链式前向星其实就是链表写法的邻接表改成数组来实现,不直接使用指针,用数组下标间接代替指针的作用
参考了链表头插法实现邻接表的思路:
1.链表中每插入一个边,是采用头插的方法,这里的思路也是头插;(第一条输入的边它的next指向就是-1,输入边的信息时它前面没有,输出时他后面也不再有边)
2.链表访问的结尾采用的是NULL,这里采用的是-1
struct edge {int to; // 边的终点int nextt; // 指向下一条边的指针int wei; // 边的权重
} edge[MAX_M]; // 存储边的数组// 添加无向边到邻接表
void addedge(int x, int y, int z) {edge[++cnt].to = y; // 边的终点edge[cnt].wei = z; // 边的权重edge[cnt].nextt = head[x]; // 将当前边指向x节点的下一条边head[x] = cnt; // 更新x节点的头结点指针 cnt表示第几条边
}
在储存图结构时我们经常会用到链式前向星.
首先在我看来它的思想像链表,只不过它是从头部插入. 我们创建一个edge的结构体,用来存储边
edge.w——权重
edge.to——这条边的终点
edge.next——它指向同一起点的上一条边,edge[MAX_M]中的下标,一个下表代表一条边
下面应该是大多数人不理解的点cnt,head数组???这个点我也是困惑了许久
讲解:1.cnt表示用来记算,表示这是第几条边 2.head数组这个表示插入的新的一条边
如head[1]=(cnt)4,指向表示以1为起点edge[4]这条边,而其他的先加入以1为起点的边就拍在了edge[4]这条边的后面去了.也就是像以一个起点建立一个链表,只不过后来加进来的边就是在头部
下面还是给道例题
P3371 【模板】单源最短路径(弱化版)
题目描述
如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。
输入格式
第一行包含三个整数 n,m,s,分别表示点的个数、有向边的个数、出发点的编号。
接下来 m 行每行包含三个整数 u,v,w,表示一条 u \to v 的,长度为 w的边。
输出格式
输出一行 n 个整数,第 i 个表示s 到第 i 个点的最短路径,若不能到达则输出 -1。
#include <stdio.h>#define MAX_N 100000
#define MAX_M 1000000
#define INF 2147483647int head[MAX_N], cnt; // 邻接表头结点数组和计数器
long long ans[MAX_M]; // 存储最短路径的数组
int vis[MAX_N]; // 记录节点是否被访问的数组struct edge {int to; // 边的终点int nextt; // 指向下一条边的指针int wei; // 边的权重
} edge[MAX_M]; // 存储边的数组// 添加无向边到邻接表
void addedge(int x, int y, int z) {edge[++cnt].to = y; // 边的终点edge[cnt].wei = z; // 边的权重edge[cnt].nextt = head[x]; // 将当前边指向x节点的下一条边head[x] = cnt; // 更新x节点的头结点指针 cnt表示第几条边
}int main() {int m, n, s;scanf("%d %d %d", &m, &n, &s); // 输入节点数、边数、起始节点for (int i = 1; i <= n; i++) {ans[i] = INF; // 初始化最短路径数组为无穷大}ans[s] = 0; // 起始节点到自身的最短路径为0for (int i = 1; i <= n; i++) {int a, b, c;scanf("%d %d %d", &a, &b, &c); // 输入边的起点、终点和权重addedge(a, b, c); // 添加边到邻接表}int pos = s; // 当前节点为起始节点while (!vis[pos]) { // 当前节点未被访问long long minn = INF; // 最小路径初始化为无穷大vis[pos] = 1; // 将当前节点标记为已访问for (int i = head[pos]; i != 0; i = edge[i].nextt) { // 遍历当前节点的邻接边if (!vis[edge[i].to] && ans[edge[i].to] > ans[pos] + edge[i].wei) { // 如果邻接节点未被访问且路径更短ans[edge[i].to] = ans[pos] + edge[i].wei; // 更新最短路径}}for (int i = 1; i <= m; i++) { // 寻找未访问节点中最小路径的节点作为下一个节点if (ans[i] < minn && !vis[i]) {minn = ans[i];pos = i;}}}for (int i = 1; i <= m; i++) {printf("%lld ", ans[i]); // 输出最短路径数组}return 0;
}