链式前向星

本文介绍了链式前向星,一种用于表示稀疏图的数据结构,通过边数组和NextEdgeArray高效处理图论问题,如计算单源最短路径。文章详细解释了其工作原理和在给定例题中的应用。

什么是链式前向星

链式前向星(Chained Forward Star)是一种用于表示稀疏图的数据结构。它主要用于解决图论中的一些算法问题,如最短路径、最小生成树等。

链式前向星通过两个数组来表示图的边和顶点信息:

  1. 边数组(Edge Array):存储了每条边的相关信息,如起点、终点、权重等。
  2. 下一个边数组(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 2147483647

int 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; // 起始节点到自身的最短路径为0

    for (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;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值