图的存储
从这里查看更详细的教程
图片和部分思路来源于 算法学习笔记(3)存图
最简单朴素的存储方式:
通过二维数组实现图的存储
若点 i 与点 j有连接则graph[i][j]=1
int graph[100][100];
int main()
{
int u,v;
cin>>u>>v;
gragp[u][v]=1;
//graph[v][u]=1; 无向图
}
对带有有权重的图 只需要将1改为存储权重w即可
int graph[100][100];
int main()
{
int u,v,w;
cin>>u>>v>>w;
gragp[u][v]=w;
//graph[v][u]=w; 无向图
}
但缺点显而易见 在图很小但是节点数较大时
需要耗费大量的空间来进行存储
在数据系数的时候绝大部分空间是被浪费了
于是很快你就能想到一个更好的解决方案:
(通过链表来实现图的存储)
但是手写链表却并不是一件容易的事情(至少对我这个蒟蒻而言是这样的)
但貌似有个list的stl可以用用(后期补上)
所以在此强烈推荐std::vector
开始之前首先建立一个Edge结构体 用来存储端点和权值
struct Edge{
int to,w;//to代表通向的端点 w代表边的权重
};
先说优点
STL里的vector容器,作为动态数组,既拥有链表节省内存的优点,但又可以以类似数组的方式访问,而且写法也很简便。(引用自算法学习笔记(3))
vector<Edge> bian[100];
void add(int from,int to ,int w)
{
bian[from].push_back(edge{to,w});
}
int main()
{
int n;
cin>>n;//循环读入n组数据
int x,y,z;
for(int i=1;i<=n;i++)
{
cin>>x>>y>>z;
add(x,y,z);//建图
}
这样经过遍历 你就能得到一张全新的关系图(一张链式存储的关系图)
遍历方法可以使用常规的数组遍历方法 遍历二维数组 也可以通过auto 来遍历
for(int j=0;j<=5;j++){
for(int i=0;i<bian[j].size();i++)
{
cout<<bian[j][i].to<<" ";
}
cout<<endl;
}
链式前向星
(貌似是一个更简单但是写起来复杂的高端存储方式?//至少在我还没理解这个过程的情况下 我觉得前向星还是蛮复杂的)
看这篇文章吧 写的稍微清晰一点 深度理解链式前向星
当然也可以看看原作者
链式前向星其实就是静态建立的邻接表,时间效率为O(m),空间效率也为O(m)。遍历效率也为O(m) 引用自大佬文章
先来一组数据
1 2
2 3
3 4
1 3
4 1
1 5
4 5
构建的图如下
首先先介绍前向星
前向星是一种特殊的边集数组,我们把边集数组中的每一条边按照起点从小到大排序,如果起点相同就按照终点从小到大排序,
并记录下以某个点为起点的所有边在数组中的起始位置和存储长度,那么前向星就构造好了.
说明几个数组的意义
用len[i]来记录所有以i为起点的边在数组中的存储长度.
用head[i]记录以i为边集在数组中的第一个存储位置.
编号: 1 2 3 4 5 6 7
起点u: 1 1 1 2 3 4 4
终点v: 2 3 5 3 4 1 5
而后得到下面的head和len
head[1] = 1 len[1] = 3
head[2] = 4 len[2] = 1
head[3] = 5 len[3] = 1
head[4] = 6 len[4] = 2
如果用链式前向星,就可以避免排序.
我们建立边结构体为:
引用自 深度理解链式前向星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++;
}
其实手动模拟一下可能会对更容易理解链式前向星
Oiwiki上的参考代码
// head[u] 和 cnt 的初始值都为 -1
void add(int u, int v) {
nxt[++cnt] = head[u]; // 当前边的后继
head[u] = cnt; // 起点 u 的第一条边
to[cnt] = v; // 当前边的终点
}
// 遍历 u 的出边
for (int i = head[u]; ~i; i = nxt[i]) { // ~i 表示 i != -1
int v = to[i];
}
链式前向星的模板
#include <iostream>
#include <vector>
using namespace std;
int n, m;
vector<bool> vis;
vector<int> head, nxt, to;
void add(int u, int v) {
nxt.push_back(head[u]);
head[u] = to.size();
to.push_back(v);
}
bool find_edge(int u, int v) {
for (int i = head[u]; ~i; i = nxt[i]) { // ~i 表示 i != -1
if (to[i] == v) {
return true;
}
}
return false;
}
void dfs(int u) {
if (vis[u]) return;
vis[u] = true;
for (int i = head[u]; ~i; i = nxt[i]) dfs(to[i]);
}
int main() {
cin >> n >> m;
vis.resize(n + 1, false);
head.resize(n + 1, -1);
for (int i = 1; i <= m; ++i) {
int u, v;
cin >> u >> v;
add(u, v);
}
return 0;
}