链式前向星

链式前向星

在之前先来看一下边集数组。

边集数组是图的表示法的一种,前向星是边集数组的一种,链式前向星是前向星的一种。

前向星

前向星是把边的起点从小到大排序,起点一样按同样的规则排终点。所以前向星使用之前要\(O(nlog_2n)\)排序一下。

使用两个数组,一个head,head[i]记录以i为起点的边在边的数组里面的第一个位置,一个len,len[i]记录以

i为起点的边有多少条。

比如:1->2->3,2->4,2->5,3->5

edge 1---------2--------3---------4-------5

--------1->2----2->3----2->4----2->5----3->5

head 1-------2--------3----------4---------5

-------1-------2--------5----------0--------0

len 1-------2--------3----------4--------5
------1-------3---------1----------0--------0

链式前向星

传说前向星使用不是那么方便,而链式前向星可以更快更简洁的实现dfs,spfa等。

链式前向星的表示是这样的:

还是需要一个边集数组edge记录边,边的属性有:边的终点to,这条边指向上一条边在edge里的编号next

一个数组head,head[i]里面装的是以i为起点的最后添加的一条边的编号

实际上这样表示就可以通过一个head[i]来访问i指向的最后一条边,然后最后一条边的next属性存放着i指向的倒数第二条边,然后倒数第二条边的next指向i指向的倒数第三条边,一个类似于链表的结构可以逆序访问i节点指向的所有边。

相关代码如下:

//链式前向星
int n;
int head[max_n];
struct edge
{
    int to;//指向的节点
    int next;//上一条边的标号
    int w;//边的权重
}e[max_n<<1];//边数组
int cnt = 0;//记录边标号
void add(int u,int v) 
{
    e[++cnt].to = v;//边的指向节点
    e[cnt].next = head[u];//指向u节点为起点的上次添加的一条边
    head[u] = cnt;//更新u节点开始的最后加入的一条边的编号
}
void print(int s)
{
    for(int i = head[s];i;i=e[i].next)//逆序输出
    {
        cout << e[i].to << " ";
    }
    cout << endl;
}

实例可见参考文章。

下面是链式前向星实现dfs和spfa的代码模板

void spfa(int x)//链式前向星的spfa
{
    d[x] = 0;
    for(int i = 0;i<n;i++)
    {
        d[i] = INF;
    }
    queue<int> que;
    que.push(x);
    inq[x] = 1;
    while(!que.empty())
    {
        int k = que.front();
        que.pop();
        inq[k] = 0;//队列中已无k节点
        for(int i = head[k];i;i=e[i].next)//i为边的标号
        {
            int j = e[i].to;//j为节点标号
            if(d[j]>d[k]+e[i].w) //k到j的距离可以松弛
            {
                d[j] = d[k]+e[i].w;
                if(!inq[j])//若队列中无j节点,加入
                {
                    que.push(j);
                }
            }
        }
    }
}
void dfs(int x,int from)//链式前向星的dfs
{
    for(int i = head[x];i;i=e[i].next)
    {
        if(e[i].to==from) continue;//不重复dfs
        //process
        dfs(e[i].to,x);
    }
}

代码汇总:

#include <iostream>
#include <queue>
#define max_n 1005
#define INF 0x3f3f3f3f
using namespace std;
int d[max_n];//最短距离
int inq[max_n];//标记数组
//链式前向星
int n;
int head[max_n];
struct edge
{
    int to;//指向的节点
    int next;//下一条边的标号
    int w;//边的权重
}e[max_n<<1];//边数组
int cnt = 0;//记录边标号
void add(int u,int v) 
{
    e[++cnt].to = v;//边的指向节点
    e[cnt].next = head[u];//u节点开始指向上次添加的一条边
    head[u] = cnt;//u节点开始的最后加入的一条边
}
void print(int s)
{
    for(int i = head[s];i;i=e[i].next)
    {
        cout << e[i].to << " ";
    }
    cout << endl;
}
void spfa(int x)//链式前向星的spfa
{
    d[x] = 0;
    for(int i = 0;i<n;i++)
    {
        d[i] = INF;
    }
    queue<int> que;
    que.push(x);
    inq[x] = 1;
    while(!que.empty())
    {
        int k = que.front();
        que.pop();
        inq[k] = 0;//队列中已无k节点
        for(int i = head[k];i;i=e[i].next)//i为边的标号
        {
            int j = e[i].to;//j为节点标号
            if(d[j]>d[k]+e[i].w) //k到j的距离可以松弛
            {
                d[j] = d[k]+e[i].w;
                if(!inq[j])//若队列中无j节点,加入
                {
                    que.push(j);
                }
            }
        }
    }
}
void dfs(int x,int from)//链式前向星的dfs
{
    for(int i = head[x];i;i=e[i].next)
    {
        if(e[i].to==from) continue;//不重复dfs
        //process
        dfs(e[i].to,x);
    }
}
int main()
{
    add(1,2);
    add(1,3);
    add(1,4);
    print(1);
    return 0;
}

参考文章:

Dreamers_Boy,【链式前向星+存图】讲解,https://blog.csdn.net/lookqaq/article/details/81304637

posted @ 2019-08-03 11:21  小张人  阅读(302)  评论(0编辑  收藏  举报
分享到: