链式前向星
在之前先来看一下边集数组。
边集数组是图的表示法的一种,前向星是边集数组的一种,链式前向星是前向星的一种。
前向星
前向星是把边的起点从小到大排序,起点一样按同样的规则排终点。所以前向星使用之前要\(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