C++之链式前向星

article/2025/10/16 12:00:04

链式前向星

  • 基本定义以及实现
  • 链式前向星实现
    • DFS
    • BFS

基本定义以及实现

(若果有一定了解,可以跳过第一部分,直接看第二部分)
我们首先来看一下什么是前向星.
前向星是一种特殊的边集数组,我们把边集数组中的每一条边按照起点从小到大排序,如果起点相同就按照终点从小到大排序,
并记录下以某个点为起点的所有边在数组中的起始位置和存储长度,那么前向星就构造好了.

用**len[i]来记录所有以i为起点的边在数组中的存储长度.
head[i]**记录以i为边集在数组中的第一个存储位置.

那么对于下图:
在这里插入图片描述
我们输入边的顺序为:

1 2

2 3

3 4

1 3

4 1

1 5

4 5

那么排完序后就得到:

编号: 1 2 3 4 5 6 7

起点u: 1 1 1 2 3 4 4

终点v: 2 3 5 3 4 1 5

得到:

head[1] = 1 len[1] = 3
head[2] = 4 len[2] = 1
head[3] = 5 len[3] = 1
head[4] = 6 len[4] = 2

但是利用前向星会有排序操作,如果用快排时间至少为O(nlog(n))

如果用**链式前向星,**就可以避免排序.

我们建立边结构体为:

struct Edge
{int next;int to;int w;
};

其中edge[i].to表示第i条边的终点,edge[i].next表示与第i条边同起点的下一条边的存储位置,edge[i].w为边权值.

另外还有一个数组head[],它是用来表示以i为起点的第一条边存储的位置,实际上你会发现这里的第一条边存储的位置其实

在以i为起点的所有边的最后输入的那个编号.

head[]数组一般初始化为-1,对于加边的add函数是这样的:

void add(int u,int v,int w)
{edge[cnt].w = w;edge[cnt].to = v;edge[cnt].next = head[u];head[u] = cnt++;
}

初始化cnt = 0,这样,现在我们还是按照上面的图和输入来模拟一下:

edge[0].to = 2; edge[0].next = -1; head[1] = 0;
edge[1].to = 3; edge[1].next = -1; head[2] = 1;
edge[2].to = 4; edge[2],next = -1; head[3] = 2;
edge[3].to = 3; edge[3].next = 0; head[1] = 3;
edge[4].to = 1; edge[4].next = -1; head[4] = 4;
edge[5].to = 5; edge[5].next = 3; head[1] = 5;
edge[6].to = 5; edge[6].next = 4; head[4] = 6;

很明显,head[i]保存的是以i为起点的所有边中编号最大的那个,而把这个当作顶点i的第一条起始边的位置.

这样在遍历时是倒着遍历的,也就是说与输入顺序是相反的,不过这样不影响结果的正确性.

比如以上图为例,以节点1为起点的边有3条,它们的编号分别是0,3,5 而head[1] = 5

我们在遍历以u节点为起始位置的所有边的时候是这样的:

for(int i=head[u];~i;i=edge[i].next)

那么就是说先遍历编号为5的边,也就是head[1],然后就是edge[5].next,也就是编号3的边,然后继续edge[3].next,也

就是编号0的边,可以看出是逆序的.

模板代码1:

 1 #include <cstdio>2 #include <cstring>3 #include <algorithm>4 #include <queue>5 using namespace std;6 const int inf = 0x3f3f3f3f;7 const int M = 4444;8 int d[M],head[M],vis[M];9 struct nod{
10     int nex,to,w;
11 }eg[M];
12 typedef pair<int,int> P;
13 int cnt=0;
14 inline void add(int u,int v,int w){
15     eg[cnt].to=v;
16     eg[cnt].w=w;
17     eg[cnt].nex=head[u];
18     head[u]=cnt++;
19 }
20 void dijkstra(int s){
21     priority_queue<P,vector<P>,greater<P> >que;
22     
23     d[s]=0;
24     que.push(P(0,s));
25     while(!que.empty()){
26         P p = que.top();
27         que.pop();
28         int v=p.second;
29         if(d[v]<p.first) continue;
30         for(int i=head[v];~i;i=eg[i].nex){
31             nod e=eg[i];
32             if(e.w+d[v]<d[e.to]){
33                 d[e.to]=e.w+d[v];
34                 que.push(P(d[e.to],e.to));
35             }
36         }
37     }
38 }
39 int main(){
40     int t,n;
41     scanf("%d %d",&t,&n);
42     memset(d,inf,sizeof(d));
43     memset(head,-1,sizeof(head));
44     for(int i=0;i<t;i++){
45         int u,v,cost;
46         scanf("%d %d %d",&u,&v,&cost);
47         add(u,v,cost);
48         add(v,u,cost);
49     }
50     dijkstra(1);
51     printf("%d\n",d[n]);
52     return 0;
53 }

模板代码2(优化):

 1 #include<iostream>2 #include<cstring>3 #include<cmath>4 #include<string>5 #include<vector>6 #include<algorithm>7 #include<cstdio>8 #include<queue>9 using namespace std;
10 const int MAX_V = 200010;
11 const int MAX_E = 2000010;
12 const int INF = 0x3f3f3f3f;
13 int V,E,cnt;
14 int heap[MAX_V],dis[MAX_V];
15 
16 struct Edge{
17     int to,next,cost;
18 }rng[MAX_E];
19 void add(int u,int v,int cost){
20     rng[cnt].to = v;
21     rng[cnt].next = heap[u];
22     rng[cnt].cost = cost;
23     heap[u] = cnt++;
24 }
25 struct Rule{
26     bool operator()(int &a,int &b)const{
27         return dis[a] > dis[b];
28     }
29 };
30 inline int read()
31 {
32     int X=0,w=1; char ch=0;
33     while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
34     while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
35     return X*w;
36 }
37 void Dijkstra(int a_){
38     memset(dis,INF,sizeof(dis));
39     priority_queue<int,vector<int>,Rule > q;
40     dis[a_] = 0;q.push(a_);
41     
42     while(!q.empty()){
43         int u = q.top();q.pop();
44         for(int k=heap[u];k != -1;k = rng[k].next){
45             int &v = rng[k].to;
46             if(dis[v] > dis[u] + rng[k].cost){
47                 dis[v] = dis[u] + rng[k].cost;
48                 q.push(v);
49             }
50         }
51     }
52 }
53 int main(void){
54     cnt = 0;
55     memset(heap,-1,sizeof(heap));
56     V = read(),E = read();
57     int x,y,z;
58     for(int i=1;i<=E;i++){
59         x = read(),y = read(),z = read();
60         add(x,y,z);
61     }
62     Dijkstra(1);
63     if(dis[V] == INF){
64         printf("-1\n");
65     }
66     else
67         printf("%d\n",dis[V]);
68     return 0;
69 }

链式前向星实现

DFS

图的深度优先遍历,基本思想是访问顶点,然后访问v0邻接到的未被访问的点顶点v1,在从v1出发递归地按照深度优先的方式遍历。当遇到一个所有邻接于它的顶点都被访问了的顶点u时,则回到已访问顶点的序列中最后一个拥有未被访问的相邻顶点的顶点w,从w出发继续访问。最终当任何已被访问的顶点都没有未被访问的相邻顶点时,遍历结束。也就是说深度优先遍历是沿着图的某一条分支遍历,直到末端,然后回溯,沿着另一条分支进行同样的遍历,直到所有的分支都被遍历过为止。

基于链式前向星的代码:

bool s[maxn]={0};//初始化
void dfs(int x)
{s[x]=true;//表示被访问过printf("%d\n",x);int i;for(i=head[x];x!=-1;i=edge[i].next){if(s[edge[i].to])dfs(edge[i].to);}
}
#include <stdio.h>
#include <memory.h>
#include <math.h>
#include <cstring>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <algorithm>
#include <map>#define I scanf
#define OL puts
#define O printf
#define F(a,b,c) for(a=b;a<c;a++)
#define FF(a,b) for(a=0;a<b;a++)
#define FG(a,b) for(a=b-1;a>=0;a--)
#define LEN 1000000
#define MAX 1<<30
#define V vector<int>using namespace std;int head[LEN];    //记录源点u在mp中第一个地址i=head[u] 调用完之后就可以用mp[i]访问边表mp 
int cnt=0;        //边表下标,随着数据的录入而扩张 
struct edge{    //边 int to,next,w;
};
edge mp[LEN];    //边表 void add(int u,int v,int w){    //增加边 mp[cnt].to=v;mp[cnt].w=w;mp[cnt].next=head[u];    //指向源点u所构成的静态链表的头结点。如果是首次构造链,head[u]=-1 ,相当于NULL head[u]=cnt++;            //更新当前地址 
}int vis[LEN];
int bk;void dfs(int s){vis[s]=1;int i;for(i=head[s];~i;i=mp[i].next){int to=mp[i].to;if(!vis[to] && to!=bk)dfs(to);}
}int main(){
//    freopen("battle over city.txt","r",stdin);fill(head,head+LEN,-1);int n,m,k,a,b,i;I("%d%d%d",&n,&m,&k);while(m--){I("%d%d",&a,&b);add(a,b,1);add(b,a,1);}while(k--){I("%d",&bk);memset(vis,0,sizeof vis);int ans=0;F(i,1,n+1) if(i!=bk && !vis[i]){dfs(i);ans++;}O("%d\n",ans-1);}return 0;}

BFS

图的宽度优先遍历与深度优先遍历不同之处在于:宽度优先遍历访问顶点v0然后访问v0邻接的未被访问的所有顶点,v1,v2,····vn,在依次访问v1,v2,···vn连接到的未被访问的所有顶点,如此下去,直到所有点都被访问。
代码:

 bool s[maxn];void bfs(int x){int queue[maxn];int iq=0;queue[iq++]=x;int i=k;for(int i=0;i<iq;iq++){//每次从队头取节点,一定是下一个待扩展节点;printf("%d\n",queue[i]);//遍历与当前节点相连接的节点,如果没有未被访问过,入队for(k=head[queue[i]];k!=-1;k=edge[k].next){if(!s[edge[k].to){queue[iq++]=edge[k].to;}}}}
#include <stdio.h>
#include <memory.h>
#include <math.h>
#include <cstring>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <algorithm>
#include <map>#define I scanf
#define OL puts
#define O printf
#define F(a,b,c) for(a=b;a<c;a++)
#define FF(a,b) for(a=0;a<b;a++)
#define FG(a,b) for(a=b-1;a>=0;a--)
#define LEN 100000
#define MAX 1<<30
#define V vector<int>using namespace std;int head[LEN];
int vis[LEN];
int cnt=0;
struct edge{int to,w,next;
};
edge mp[LEN];void add(int u,int v,int w){mp[cnt].to=v;mp[cnt].w=w;mp[cnt].next=head[u];head[u]=cnt++;
}int main(){
//    freopen("Forwards on Weibo.txt","r",stdin);int N,L,M,K,i,j;fill(head,head+LEN,-1);I("%d%d",&N,&L);F(i,1,N+1){I("%d",&M);while(M--){I("%d",&j);add(j,i,1);}}I("%d",&K);while(K--){I("%d",&i);memset(vis,0,sizeof vis);queue<int> q;q.push(i);int lev=0,cnt=0;while(!q.empty()){int sz=q.size();while(sz--){int u=q.front();q.pop();vis[u]=1;for(j=head[u];~j;j=mp[j].next){int to=mp[j].to;if(!vis[to]){vis[to]=1;q.push(to);cnt++;}}}lev++;if(lev>=L) break;}O("%d\n",cnt);}return 0;}

转自:http://blog.csdn.net/acdreamers/article/details/16902023

http://blog.csdn.net/henuwhr/article/details/76668590


http://chatgpt.dhexx.cn/article/U96DtFp9.shtml

相关文章

图的存储 —— 链式前向星

图的存储 —— 链式前向星 链式前向星采用了一种静态链表存储方式&#xff0c;将边集数组和邻接表相结合&#xff0c;可以快速访问一个节点的所有邻接点&#xff0c;在算法竞赛中被广泛应用。 链式前向星有如下两种存储结构。 边集数组&#xff1a;edge[]&#xff0c;edge[i…

简述链式前向星

一、前言 我们常见的存图数据结构有两种&#xff0c;一种是邻接矩阵&#xff0c;另一种是邻接表。而在邻接矩阵中&#xff0c;空间复杂度为 O ( n 2 ) O(n^2) O(n2)&#xff0c;在稀疏图的情况下&#xff0c;相较于邻接表浪费了许多空间。而常规的邻接表是用链表进行操作&…

链式前向星 详解

链式前向星 链式前向星是一种类似于邻接表的存图方式&#xff0c;同样适用于有向 图和无向图。 他建立的是边与边之间的联系 它将边里的所有边都进行编号 int cnt; //边的编号 struct edge{ //边的结构体int from,to,w,next; //from是边的起点&#xff08;这个可有可无&a…

用链式前向星存储图

图最常用的存储结构主要是邻接矩阵和邻接表。当顶点数太大时&#xff0c;用二维数组表示的邻接矩阵可能超内存&#xff08;MLE&#xff09;&#xff0c;而用邻接表的编码工作量较大&#xff0c;此时&#xff0c;使用vector数组或链式前向星模拟邻接表是不错的选择。因vector数组…

链表、链式前向星

讲链表的时候就卡在这里了&#xff0c;最短路又卡在链式前向星上了&#xff0c;毕竟是图论基础&#xff0c;觉得还是有必要写一写防止下次再懵。 链表都是头插法&#xff01;&#xff01;即每次我们给他插一个头。 普通链表 先初始化令head-1,idx-1. void add_tohead(int x…

链式前向星(详细讲述)

在dalao的压迫下本蒟蒻发个博客&#xff0c;给大家讲一下链式前向星&#xff08;新手&#xff0c;写错了轻喷&#xff09; 首先说明一点链式前向星适合于稀疏图&#xff0c;而邻接矩阵则更适合稠密图&#xff0c;所以最好看好数据范围再决策使用哪个方法&#xff0c;当然有些题…

链式前向星与邻接表对比

本文图片及数据 对于这样一张有向图&#xff1a; 输入边的顺序如下&#xff1a; 1 2 2 3 3 4 1 3 4 1 1 5 4 5 对于邻接表来说是这样的&#xff1a; 1 -> 2 -> 3 -> 5 2 -> 3 3 -> 4 4 -> 1 -> 5 5 ->^ 对于链式前向星来说是这样的&…

链式前向星存图(有图详解)

链式前向星:既然是链式那么肯定和链表相关,前向星是每次指向都向前 链式前向星存图是以边为中心,并不是以结点为中心,它记录的是边的一些属性,包括边边的id、头节点、尾结点、权值、边的指向! 边的指向是遍历图的时候需要按照一定顺序去遍历,而不能胡乱的去遍历,那么就需要这些…

链式前向星的详解

目录 1&#xff1a;链式前向星的概念解释 2&#xff1a;代码展现 链式前向星是一种非常好用的有向图存储方式&#xff0c;但是它的代码难度却有点大。作者在这上面花了很长的时间才弄懂。虽然有其他的博客大佬写的很好&#xff0c;但总感觉不太适合小白选手&#xff08;比如说…

链式前向星基本原理

一、概述 我们在学习图论的时候学习了一种图的存储结构--二维数组邻接矩阵储存&#xff0c;他虽然可以表达直观&#xff0c;快速访问连接两点的边&#xff0c;但是它占用空间大&#xff0c;只适用于点少的图&#xff0c;所以我们需要一种能够可以存储大型图的东西--链式前向星…

链式前向星的原理图解

笔者写下这篇文章的契机是&#xff0c;前两天在上机课用纯链式存储写题&#xff0c;累的够呛。于是痛定思痛&#xff0c;在此梳理一遍链式前向星&#xff0c;也希望能给有同样困扰的同学们提供一点帮助。 在进入正题之前&#xff0c;我们先用邻接矩阵和邻接表引入。 邻接矩阵表…

【链式前向星+存图】讲解

大佬的链式前向星:https://blog.csdn.net/acdreamers/article/details/16902023 【前向星】&#xff1a; 解释一下&#xff1a; 【前向星和链式前向星的不同】&#xff1a; 【给出链式前向星的代码实现】&#xff1a; #include<bits/stdc.h> using namespace std; #def…

链式前向星——最完美图解

图的存储方法很多,最常见的除了邻接矩阵、邻接表和边集数组外,还有链式前向星。链式前向星是一种静态链表存储,用边集数组和邻接表相结合,可以快速访问一个顶点的所有邻接点,在算法竞赛中广泛应用。 链式前向星存储包括两种结构: 边集数组:edge[ ],edge[i]表示第i条边…

链式前向星(详解)

链式前向星&#xff08;详解&#xff09; 转自&#xff1a;传送门 我们首先来看一下什么是前向星. 前向星是一种特殊的边集数组,我们把边集数组中的每一条边按照起点从小到大排序,如果起点相同就按照终点从小到大排序, 并记录下以某个点为起点的所有边在数组中的起始位置和…

链式前向星

一 点睛 链式前向星采用了一种静态链表存储方式&#xff0c;将边集数组和邻接表相结合&#xff0c;可以快速访问一个节点的所有邻接点。 链式前向星有如下两种存储结构。 1 边集数组 edge[],edge[i] 表示第 i 条边。 2 头节点数组 head[],head[i] 存储以 i 为起点的第1条边…

深度理解链式前向星

我们首先来看一下什么是前向星. 前向星是一种特殊的边集数组,我们把边集数组中的每一条边按照起点从小到大排序,如果起点相同就按照终点从小到大排序, 并记录下以某个点为起点的所有边在数组中的起始位置和存储长度,那么前向星就构造好了. 用len[i]来记录所有以i为起点的边在数…

链式前向星(超详细)

前向星和链式前向星 链式前向星 添加边的操作 链式前向星&#xff0c;就是以链式结构来储存前向星&#xff0c;每条边就要用结构体&#xff0c;结构体中包含三个数据&#xff1a; Edge[i].to &#xff1a;第i条边的终点 Edge[i].next &#xff1a;与第i条边同一个起点的下…

stm32串口中断的接收

利用串口使得led点亮 利用之前的串口函数加上NVIC的中断函数结构体 定义结构体 定义 配置抢占优先级的组别 配置NVIC串口中断的结构体&#xff1a;中断的通道&#xff0c;配置抢占优先级和子优先级 使能CMD 结构体初始化 还有需要配置中断串口的配置&#xff1a; 串口 接…

STM32串口下载

使用FlyMCU下载程序 1.上电前&#xff0c;设置BOOT01&#xff0c;BOOT10。或者是在上电后&#xff0c;设置BOOT01&#xff0c;BOOT10之后&#xff0c;然后按一下复位按键&#xff0c;从而通过串口下载程序。 2.&#xff0c;在MDK编译加载生成的hex文件&#xff0c;并勾选右边的…

STM32 串口发送乱码问题

STM32 串口发送乱码问题 一、问题状况&#xff1a; 显示为一堆乱码&#xff0c;&#x1f4a2;&#x1f620;&#x1f4a2;晕啊 。 二、解决方法 (通常问题是出在step3:调整外部振荡器默认值) step1:检查时钟树配置 设置晶振为开发板上外部晶振一致的8MHz。 step2:检查波…