在学习图的存储的时候,我们通常会有邻接矩阵和邻接表两种选择,邻接矩阵好写但效率低,邻接表不好写写但效率高,这里有一种优秀的数据结构,那便是用静态链表头插法实现的链式向前星。
先给出一下邻接矩阵
可以看出有向图中有4个顶点4条边
1 —> 2
1 —> 3
2 —> 4
3 —> 1
那么用链式向前星该怎么存呢?
字丑的见不得人啦,对于每个顶点u,我们都会有h[u]代表其指向的第一个边的idx,然后e[idx]是指边v,w[idx]指边u-v的权重,ne[idx]是指链上的下一条边,当ne[i] = -1时表示链完
对于上面的图
h[1] = 1, e[1] = 3, ne[1] = 0, e[0] = 2, ne[0] = -1;
h[2] = 2, e[2] = 4, ne[2] = -1;
h[3] = 3, e[3] = 1, ne[3] = -1;
h[4] = -1;
刚开始我也觉得难理解,但是敲代加画图加脑海模拟便会了,这个是头插法的静态链表,顶点u指向的多个顶点v都在链表中,而链表头就是h[u]; 当h[u] == -1 || ne[idx] == -1就表示链表到空了,看看代码
定义
// N 表顶点个数 M 表边个数 视题目而定
const int N = 1010, M = 1e4 + 10;
//这是有向边
//e[idx]存的是u指向的顶点v, w[idx]存的是 u - v之间的权重
//ne[idx] 存的是下一顶点所在的下标 h[u] 就是头结点 idx是游标
int e[M], w[M], ne[M], h[N], idx;
//无向边是特殊的有向边 开双倍
//int e[M<<1], w[M<<1], ne[M<<1], h[N], idx;
初始化
void init() //初始化
{
idx = 0; //游标从零开始
memset(h, -1, sizeof h); //将所有头结点置空
}
加边操作
void add(int a, int b, int c) //加边操作
{
e[idx] = b; //存顶点值
w[idx] = c; //存边权
ne[idx] = h[a]; //存h[a]指向的前驱顶点的下标
h[a] = idx++; //h[a]指向b所在下标, idx游标往后移动
}
总揽
#include <iostream>
#include <cstring>
using namespace std;
// N 表顶点个数 M 表边个数 视题目而定
const int N = 1010, M = 1e4 + 10;
//这是有向边
//e[idx]存的是u指向的顶点v, w[idx]存的是 u - v之间的权重
//ne[idx] 存的是下一顶点所在的下标 h[u] 就是头结点 idx是游标
int e[M], w[M], ne[M], h[N], idx;
//无向边是特殊的有向边 开双倍
//int e[M<<1], w[M<<1], ne[M<<1], h[N], idx;
void init() //初始化
{
idx = 0; //游标从零开始
memset(h, -1, sizeof h); //将所有头结点置空
}
void add(int a, int b, int c) //加边操作
{
e[idx] = b; //存顶点值
w[idx] = c; //存边权
ne[idx] = h[a]; //存h[a]指向的前驱顶点的下标
h[a] = idx++; //h[a]指向b所在下标, idx游标往后移动
}
int main()
{
int n, m; //顶点数(1~n)和权值
cin >> n >> m;
init(); //别忘初始化
while(m--)
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c); //加边
}
for(int i = 1; i <= n; ++i) //一个一个顶点遍历
{
for(int j = h[i]; j != -1; j = ne[j]) //遍历顶点指向的边
cout << e[j] << "(" << w[j] << ") ";
cout << "\n\n";
}
return 0;
}
遍历结果如下:
>Tips:有想一起交流学习的可以互关 有错误请指出!