链式前向星

 

 

 一,前言

  说实话,看到时候没有看懂,自己模拟了一下边是怎么加上去的,才搞明白。想明白之后,才知道被这些变量名给忽悠了,我不知道取名的人是怎么想的,但我觉的有些变量名并不恰当,直接把我带到沟里去了,所以这里我写的变量名是根据自己的理解命名。๑乛◡乛๑ (●´∀`●)

 

 

二,引子

1,

  想要通过一组有序的点来记录一棵树的话,你只需要两个变量,一个记录子节点的编号,一个记录父节点的编号。但如果存的是一张图,又有什么不同呢?

在一棵树中,子节点只有一个父节点,而如果是图的话,子节点就可能有多个父节点,出现 一点映射多点的情况,这样你就不知道要设几个变量来存前一个点的编号了。

2,

  为了解决这个问题,有人想出了用足够多的空间存父节点的编号——邻接矩阵。

  但邻接矩阵的时间和空间复杂度都不够理想,于是又有人想出邻接矩阵的优化——邻接表。

  但邻接表写起来又太麻烦了,于是又有人想出另外一种办法存图——链式前向星。

3,

  邻接矩阵和邻接表都是根据存点的信息再推出边的信息,进而存图,而链式前向星是直接存边的信息(边的信息自然包括顶点的信息),然后直接由边的信息推出一张图。

 

 

三,链式前向星

1,那么一条边需要存什么信息呢?

  很容易想到的是:起点,终点,权值。但是有个问题:如何通过这条边得到它的所有相邻边的信息?

如果是通过这条边的终点去匹配其他所有边的起点的,其本质也就跟邻接矩阵差不多。因此,链式前向星并不是简单的存上述三种变量,那么,它需要存的是什么信息呢?

 

2,邻接边的链接    o_o ....

  链式前向星首先对所有的边进行编号;

  然后在每条边中,链式前向星放弃了起点的保存,并在每一条边存的信息加上一个值 pre —— 当前边的起点的所有邻接边中,位于当前边的前一条邻接边的编号;

  于是,为了找到某个点的所有邻接边中最后一条边的编号,我们需要另外设一个数组 last[]。last[i] —— 点i 的所有邻接边中最后一条边的编号;

  最后,以BFS为例:

    先用 last[i] 得到根节点的所有邻接边中最后一条边的编号,

    再利用 pre 往前遍历所有的邻接边,与此同时,将这些邻接边的终点放入队列中,作为下次搜索的起点。

    最后,不断取出队列中的点,重复上述操作。

 

3,总结

  ① 我们需要 一个结构体数组 e 记录边的信息,结构体里面的变量有:pre,to,w。

对于第 i 条边 e[i] 里的变量来说,有:

  to:  编号为 i 的边是以某个点为起点的第 x 条边,其对应的终点为—— to

  w:  编号为 i 的边是以某个点为起点的第 x 条边,其对应的权值为—— w

  pre:编号为 i 的边是以某个点为起点的第 x 条边,其对应的同起点的第 x-1 条边编号为—— pre

其中:我们根本不 care x 的值是多少,只需知道它能通过 pre 往前递推就是了。

  ②  last[]:last[i] —— 点i 的所有邻接边中最后一条边的编号。

 

 

四,利用链式前向星遍历图

利用 pre 进行横向搜索:

  以某个点为起点的所有边的遍历。

利用 last[] 和to 进行向下搜索:

  通过某条边的终点,找到以该边终点为起点的所有邻接边中最后一条边的编号。

 

 

五,完整代码:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Chain_forward_star
{
#define N (int)2e6+5
    int last[N], tot;
    struct Node {
        int pre, to, w;
    }e[N];

    void init()  // 初始化
    {
        tot = 0;     // 链式前向星中边的数量,初始为 0
        memset(last, -1, sizeof(last));
    }
    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;        // 更新 last 数组,新加入的边成为当前边的起点的所有邻接边中最后一条边的编号
    }
}star;

int vis[N];
void dfs(int s)   // 遍历图
{
    vis[s] = 1;
    for (int i = star.last[s]; ~i; i = star.e[i].pre)  // 遍历以 s 为起点的所有边
    {
        printf("编号为 %d 的边: 起点:%d 终点:%d 权值:%d\n", i, s, star.e[i].to, star.e[i].w);
        if (!vis[star.e[i].to])                   // 以第 i 条边的终点为起点,向下搜索
            dfs(star.e[i].to);
    }
}
int main(void)
{
    int m; // 边数
    while (scanf("%d", &m) != EOF)
    {
        star.init();
        memset(vis, 0, sizeof(vis));
        for (int i = 1; i <= m; i++)
        {
            int x, y, z; scanf("%d%d%d", &x, &y, &z);
            star.add(x, y, z);
        }
        dfs(1);  // 从 点1 开始遍历
    }
    return 0;
}
/*
测试数据:有向图
3
1 2 1
2 3 2
2 4 3
*/
View Code

 

 

 

========== ========= ========= ======= ===== ===== === == =

  渡荆门送别  唐 李白 

渡远荆门外,来从楚国游。

山随平野尽,江入大荒流。

月下飞天镜,云生结海楼。

仍怜故乡水,万里送行舟。

 

 

posted @ 2020-03-16 17:23  叫我妖道  阅读(560)  评论(0编辑  收藏  举报
~~加载中~~