链式前向星存图法

之前在学习最大流的时候,EK算法使用的是一个二维数组,但是对于稀疏图,二维数组比较浪费空间,对于稠密图,EK算法又解决不了,使用Dinic的时候需要一种比较好的存图方法,下面就介绍一下存图利器——链式前向星

对于存图的各种数据结构,复杂度如下

邻接矩阵

O(1)(查询一条边) O(n)枚举出边 O(N*N)空间复杂度

前向星

O(n)(查询一条边) O(n)枚举出边 O(n)空间复杂度 (预处理时
间复杂度(O(NlgN)))

链式前向星

O(n)(查询一条边) O(n)枚举出边 O(n)空间复杂度

我们发现对于与一道题若n逼近10^6时,前两者的时间就不行了,而链式前向星还可以支撑这种数据规模

链式前向星的原理

在这里插入图片描述
其中edge[i].to表示第 i 条边的终点,edge[i].next更像一个指针,表示与第 i 条变同起点下一条边的存储位置,edge[i].w为权值

head[] 数组一般初始化为-1,表示与结点u有连边的第一个点的地址

void add(int u,int v,int w)
{
    edge[cnt].w    = w;
    edge[cnt].to   = v;
    edge[cnt].next = head[u];//存的是以u为起点边的下一条边
    head[u] = cnt++;//存的是以u为起点的最后一条输入的边的序号cnt
    //比如以1为起点的最后一条边的序号cnt=5,即第五条输入的边
}

初始化 cnt = 0,按照上面的图模拟输入

在这里插入图片描述
我们需要知道一点,在链式前向星中,从上到下是存储的书讯,遍历的时候需要从下到上!

我们只看以 1 为起点的存边情况(蓝色直线)

首先以 1 为起点的边 0 的下一条边为 -1 ,说明其为最后一条边;第二次储存时,以 1 为起点的边 3 的下一条边时 0 ,head储存最新的边序号为 3 ;第三次储存时,以 1 为起点的边 5 的下一条边序号为3 ,head 储存最新的边 5。

总结

head[i] 保存的是以 i 为起点的所有边中最大边的序号,把这条边当做顶点i的第一条边的起始位置,所以这样在遍历的时候就是倒着的,与输入的顺序相反,不影响结果的正确性。

代码实现

struct Edge{
    int next,to,w;//下一条边的储存下标,这条边的终点,权值
};
Edge edge[500010];
void Add(int u, int v, int w) {  //增边
	edge[++cnt].next = head[u];//cnt为计数,从1开始
	edge[cnt].to = v;
	edge[cnt].w = w;
	head[u] = cnt;//第一条边为当前边
}
for(int i=head[s]; i!=0; i=edge[i].next)  //遍历
{

}


例题 洛谷3371

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
const long long inf = 2147483647;
const int maxn = 500005;
int m,n,s,t,cnt=0;
int dis[maxn],vis[maxn],head[maxn];
struct edge
{
    int next,to,distance;
}edge[maxn];

void addedge(int a,int b,int c)
{
    edge[++cnt].next   = head[a];
    edge[cnt].to       = b;
    edge[cnt].distance = c;
    head[a]            = cnt;
}

void fff()
{
    queue<int> q;
    for(int i=1;i<=n;i++)
    {
        dis[i] = inf;
        vis[i] = 0;
    }
    q.push(s);
    dis[s] = 0;
    vis[s] = 1;
    while (!q.empty())
    {
        int u = q.front();q.pop();
        vis[u] = 0;
        for(int i=head[u];i;i=edge[i].next)
        {
            int v = edge[i].to;
            if(dis[v]>dis[u]+edge[i].distance)
            {
                dis[v] = dis[u]+edge[i].distance;
                if(vis[v]==0)
                {
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }
    
}

int main()
{
    cin>>n>>m>>s;
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        addedge(a,b,c);
    }
    fff();
    for(int i=1;i<=n;i++)
    {
        if(s==i)  cout<<0<<' ';
        else      cout<<dis[i]<<' ';

    }
    return 0;
}

参考 https://blog.csdn.net/Spidy_harker/article/details/88877072

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值