• 存图方式


    vector 存图

    struct node{
        ll to, w;
    };
    
    vector<node> t[maxn];
    
    void add(const int u, const int v, const int w)
    {
        t[u].push_back((node){v, w});
    }
    

    链式前向星存图

    如果说邻接表是不好写但效率好,邻接矩阵是好写但效率低的话,前向星就是一个相对中庸的数据结构。前向星固然好写,但效率并不高。而在优化为链式前向星后,效率也得到了较大的提升。虽然说,世界上对链式前向星的使用并不是很广泛,但在不愿意写复杂的邻接表的情况下,链式前向星也是一个很优秀的数据结构。 ——摘自《百度百科》

    链式前向星其实就是静态建立的邻接表,时间效率为O(m),空间效率也为O(m)。遍历效率也为O(m)。

    对于下面的数据,第一行5个顶点,7条边。接下来是边的起点,终点和权值。也就是边1 -> 2 权值为1

    5 7
    1 2 1
    2 3 2
    3 4 3
    1 3 4
    4 1 5
    1 5 6
    4 5 7
    

    链式前向星存的是以【1,n】为起点的边的集合,对于上面的数据输出就是:

    1 //以1为起点的边的集合
    1 5 6
    1 3 4
    1 2 1
     
    2 //以2为起点的边的集合
    2 3 2
     
    3 //以3为起点的边的集合
    3 4 3
     
    4  //以4为起点的边的集合
    4 5 7
    4 1 5
     
    5 //以5为起点的边不存在
    

    我们先对上面的7条边进行编号第一条边是1以此类推编号【1~7】,然后我们要知道两个变量的含义:

    • next,表示与这个边起点相同的上一条边的编号。
    • head[ i ]数组,表示以 i 为起点的最后一条边的编号。

    构造点的结构体:

    const int maxn=10000005;
    long long n,m,tot,head[maxn];
    
    struct node
    {
    	int to,w,next;
    }t[maxn];
    
    

    head数组一般初始化为-1,为什么是 -1后面会讲到。加边函数是这样的:

    void add(const int u,const int v,const int w)
    {//u是起点,v是终点,w是边权 
    	t[++tot].to=v; //存终点到 t.to 
    	t[tot].w=w; //存边权到 t.w 
    	t[tot].next=head[u]; //以u为起点上一条边的编号,也就是与这个边起点相同的上一条边的编号
    	head[u]=tot; //更新以u为起点的上一条边的编号。 tot是边的个数 
    }//加边操作 
    

    我们只要知道next,head数组表示的含义,根据上面的数据就可以写出下面的过程:

    对于1 2 1这条边:t[0].to = 2; t[0].next = -1; head[1] = 0;

    对于2 3 2这条边:t[1].to = 3; t[1].next = -1; head[2] = 1;

    对于3 4 3这条边:t[2].to = 4; t[2],next = -1; head[3] = 2;

    对于1 3 4这条边:t[3].to = 3; t[3].next = 0; head[1] = 3;

    对于4 1 5这条边:t[4].to = 1; t[4].next = -1; head[4] = 4;

    对于1 5 6这条边:t[5].to = 5; t[5].next = 3; head[1] = 5;

    对于4 5 7这条边:t[6].to = 5; t[6].next = 4; head[4] = 6;

    遍历函数是这样的:

    for(int i=1;i<=n;++i)
    {
    	cout<<i<<endl;
    	for(int j=head[i];j!=-1;j=t[j].next)
    	{
    		printf("%d %d %d\n",i,t[j].to,t[j].w);
    	}
    	cout<<endl;
    }
    

    第一层 for:循环每一个点。
    第二层 for:① 先让 j = 以 i 为起点的最后一条边的编号。② 输出第 j 条边的详细信息。③ 让 j = 与这个边起点相同的上一条边的编号 t[j].next 。④ 一直循环下去,直到 j = -1 ,说明你找到了最后一个边(也就是以 i 为起点的第一条边),终止条件。

    搜寻则是从最后一条边往前搜寻。充分利用了 next 和 head 。

    具体代码为:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define ll long long
    using namespace std;
    const int maxn=10000005;//点数最大值 
    
    inline ll read()
    {
    	int f=0,x=0;
    	char ch=getchar();
    	while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
    	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^=48),ch=getchar();
    	return f?-x:x;
    }
    ll n,m,tot; //n个点,m条边,tot用来计数。 
    ll head[maxn]; // head[i],表示以i为起点的第一条边在边集数组的位置(编号)
    
    struct node
    {
    	int to,w,next;
    }t[maxn];
    
    void add(const int u,const int v,const int w)
    {//u是起点,v是终点,w是边权 
    	t[++tot].to=v; //存终点到 t.to 
    	t[tot].w=w; //存边权到 t.w 
    	t[tot].next=head[u]; ////以u为起点上一条边的编号,也就是与这个边起点相同的上一条边的编号
    	head[u]=tot; //更新以u为起点的上一条边的编号。 tot是边的个数 
    }//加边操作 
    
    int main()
    {
    	n=read();
    	m=read();
    	int a,b,c;
    	for(int i=1;i<=n;++i)//初始化head数组 
    	{
    		head[i]=-1;
    	}
    	
    	for(int i=1;i<=m;++i)//输入m条边 
    	{
    		a=read();
    		b=read();
    		c=read();
    		add(a,b,c);//加边
    		/*
    		加双向边
    		add(a,b,c);
    		add(b,a,c);
    		*/ 
    	}
    	
    	for(int i=1;i<=n;++i)//遍历以i为起点的边 
    	{
    		cout<<i<<endl;
    		for(int j=head[i];j!=-1;j=t[j].next)
    		{
    			printf("%d %d %d\n",i,t[j].to,t[j].w);
    		}
    		cout<<endl;
    	}
    	return 0;
    }
    /*
    5 7
    1 2 1
    2 3 2
    3 4 3
    1 3 4
    4 1 5
    1 5 6
    4 5 7
    */
    

    参考

    https://blog.csdn.net/acdreamers/article/details/16902023#comments

    https://blog.csdn.net/ZscDst/article/details/79060993

    https://blog.csdn.net/pk__pk/article/details/78432288

    https://blog.csdn.net/sugarbliss/article/details/86495945

  • 相关阅读:
    关于编程
    Python的内建sort方法

    Elgg设置SMTP验证发送邮件教程
    ThinkPHP 和 UCenter接口的冲突
    mac下终端iTerm2配置
    自动化 Amazon EBS 快照生命周期
    AWS Certified Solutions Architect Associate 学习笔记1
    实例存储生命周期 Instance store
    可触发 Lambda 函数的 CloudFront 事件
  • 原文地址:https://www.cnblogs.com/EdisonBa/p/14948744.html
Copyright © 2020-2023  润新知