关于图的存储方法 (静态邻接表、前向星、边集数组)

34 篇文章 0 订阅

一、邻接矩阵(不多说了)  G[u][v]

二、邻接表

                  1、动态链表(指针)      一个数组表头(u)+ struct结点(v),相链,若有权值等信息再在结点里加相应域。

                  2、静态链表(数组)      first数组(模拟表头、u)+ 边集数组(编号e,u、v)+ next数组(模拟指针相指)。这个跟1看似有点区别(前者链的是点,后者链的是边),其实是没区别,因为要用数组实现链表,所以对1中所有结点实行e编号,意义就是“边”。

通常实现方法: 开五个数组 first[MAXN]; u[MAXM], v[MAXM], w[MAXM], next[MAXM]。

三、边集数组

就是把所有边放在一个数组里,这样就可以完成遍历所有边的操作了(很土吧= =)。通常要根据实际需要做一些辅助储存。

1、上面的数组实现邻接表就是边集数组再加上first数组和next数组。

2、前向星。跟1很相似的,区别是他对边集数组按u点(前一个端点)升序排序,使得由同一个点出发的边都集中在一起了。再加上辅助数组 f[MAXN](跟前面first数组类似的作用),存 结点i 出发的第一个边在边集数组里的位置。 

所以注意到,前向星其实就是做了一个紧缩存储的处理,并且通过一次排序,省掉了next数组(静态邻接表)。当然也可以不排序,多维护一个next数组。

通常实现方法:开四个数组 f[MAXN]; u[MAXM], v[MAXM], w[MAXM]。


附:

①静态邻接表+Dijkstra+heap

//  Dijkstra+静态邻接表
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;

#define MAXN 100
#define MAXM 100
#define INF 1<<30
typedef pair<int, int> pii;		//(dist[v], v)
priority_queue<pii, vector<pii>, greater<pii> > q;
int first[MAXN];
int u[MAXM], v[MAXM], w[MAXM], next[MAXM];
int dist[MAXN], ins[MAXN];	//  ins[]  是否在s集合中
int n, m;

void dijkstra(int st)
{
	for(int i=0; i<n; i++) dist[i] = i==st? 0: INF;
	memset(ins, 0, sizeof(ins));
	q.push(make_pair(dist[st], st));
//	ins[st] = 1;						//别跟spfa inq弄混,在优先队列里取出来才算是ins了
	while(!q.empty())
	{
		pii p = q.top();	q.pop();
		int x = p.second;
		if(!ins[x])
		{
			ins[x] = 1;
			for(int i=first[x]; i!=-1; i=next[i])
			{
				if(dist[v[i]] > dist[x]+w[i])		//relax
				{
					dist[v[i]] = dist[x] + w[i];
					q.push(make_pair(dist[v[i]], v[i]));
				}
			}
		}
	}//end of while
}

void read_graph()
{
	scanf("%d%d", &n, &m);
	memset(first, -1, sizeof(first));
	for(int i=0; i<m; i++)
	{
		scanf("%d%d%d", &u[i], &v[i], &w[i]);
		next[i] = first[u[i]];
		first[u[i]] = i;
	}
}

int main()
{
	read_graph();
	int st;
	dijkstra(scanf("%d", &st));
	for(int i=0; i<n; i++)
	{
		printf("[%d,%d]=%d\n", st, i, dist[i]);
	}
}

②静态邻接表+spfa

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
#define MAXN 100
#define MAXM 100
#define INF 1<<30
queue<int> q;
int first[MAXN], next[MAXM];
struct edge
{
	int u, v, w;
}a[MAXM];
int dist[MAXN], inq[MAXN];
int n, m;

void spfa(int st)
{
	for(int i=0; i<n; i++) dist[i] = i==st? 0: INF;
	memset(inq, 0, sizeof(inq));
	q.push(st);
	inq[st] = 1;					//反正马上就出队,这个inq可以不要
	while(!q.empty())
	{
		int u = q.front(); q.pop();
		inq[u] = 0;
		for(int e=first[u]; e!=-1; e=next[e])
		{
			int v = a[e].v;
			if(dist[v] > dist[u]+a[e].w)
			{
				dist[v] = dist[u]+a[e].w;
				if(!inq[v]) { q.push(v); inq[v] = 1; }		//inq=1 !!!!
			}
		}
	}
}

void read_graph()
{
	cin>>n>>m;
	memset(first, -1, sizeof(first));		//别忘了初始化 表头
	for(int e=0; e<m; e++)
	{
		cin>>a[e].u>>a[e].v>>a[e].w;
		next[e] = first[a[e].u];
		first[a[e].u] = e;
	}
}

int main()
{
	read_graph();
	int st;
	cin>>st;
	spfa(st);
	for(int i=0; i<n; i++)
	{
		printf("[%d,%d]=%d\n", st, i, dist[i]);
	}
}



  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
用于打比赛的ACM算法模板 常用函数与STL 重要公式与定理 1. Fibonacci Number 2. Lucas Number 3. Catalan Number 4. Stirling Number(Second Kind) 5. Bell Number 6. Stirling's Approximation 7. Sum of Reciprocal Approximation 8. Young Tableau 9. 整数划分 10. 错排公式 11. 三角形内切圆半径公式 12. 三角形外接圆半径公式 13. 圆內接四边形面积公式 14. 基础数论公式 大数模板,字符读入 数论算法 1. Greatest Common Divisor最大公约数 2. Prime素数判断 3. Sieve Prime素数筛法 4. Module Inverse模逆元 5. Extended Euclid扩展欧几里德算法 6. Modular Linear Equation模线性方程(同余方程) 7. Chinese Remainder Theorem中国余数定理(互素于非互素) 8. Euler Function欧拉函数 9. Farey总数 9. Farey序列构造 10. Miller_Rabbin素数测试,Pollard_rho因式分解 论算法 1. 最小生成树(Kruscal算法) 2. 最小生成树(Prim算法) 3. 单源最短路径(Bellman-ford算法) 4. 单源最短路径(Dijkstra算法) 5. 全源最短路径(Folyd算法) 6. 拓扑排序 7. 网络预流和最大流 8. 网络最小费用最大流 9. 网络最大流(高度标号预流推进) 10. 最大团 11. 二分最大匹配(匈牙利算法) 12. 带权二分最优匹配(KM算法) 13. 强连通分量(Kosaraju算法) 14. 强连通分量(Gabow算法) 15. 无向割边割点和双连通分量 16. 最小树形O(N^3) 17. 最小树形O(VE) 几何算法 1. 几何模板 2. 球面上两点最短距离 3. 三点求圆心坐标 4. 三角形几个重要的点 专题讨论 1. 树状数组 2. 字典树 3. 后缀树 4. 线段树 5. 并查集 6. 二叉堆 7. 逆序数(归并排序) 8. 树状DP 9. 欧拉路 10. 八数码 11. 高斯消元法 12. 字符串匹配(KMP算法) 13. 全排列,全组合 14. 二维线段树 15. 稳定婚姻匹配 16. 后缀数组 17. 左偏树 18. 标准RMQ-ST 19. 度限制最小生成树 20. 最优比率生成树(0/1分数规划) 21. 最小花费置换 22. 区间K大数 23. LCA - RMQ-ST 24. LCA – Tarjan 25. 指数型母函数 26. 指数型母函数(大数据) 27. 单词前缀树(字典树+KMP) 28. FFT(大数乘法) 29. 二分网络最大流最小割 30. 混合欧拉回路 31. 无源汇上下界网络流 32. 二分最小点权覆盖 33. 带约束的轨道计数(Burnside引理) 34. 三分法求函数波峰 35. 单词计数,矩阵乘法 36. 字符串和数值hash 37. 滚动队列,前向星表示法 38. 最小点基,最小权点基
以下是使用邻接表存储结构创建无向的 C++ 代码: ```c++ #include <iostream> #include <vector> using namespace std; // 邻接表存储结构中的边结构体 struct Edge { int to; // 边的终点 int weight; // 边的权重 Edge(int t, int w) : to(t), weight(w) {} }; // 邻接表存储结构中的顶点结构体 struct Vertex { int id; // 顶点编号 vector<Edge> adj; // 存储与该顶点相邻的所有边 Vertex(int i) : id(i) {} }; // 创建无向邻接表存储结构 vector<Vertex> createGraph(int n, vector<vector<int>>& edges) { vector<Vertex> graph(n); for (auto& e : edges) { int u = e[0], v = e[1], w = e[2]; graph[u].adj.emplace_back(v, w); graph[v].adj.emplace_back(u, w); } return graph; } int main() { int n = 5; // 中顶点个数 vector<vector<int>> edges = {{0, 1, 2}, {0, 4, 1}, {1, 2, 3}, {1, 3, 4}, {2, 3, 5}}; // 边集合 auto graph = createGraph(n, edges); cout << "邻接表存储如下:" << endl; for (int i = 0; i < n; ++i) { cout << "顶点" << i << "的邻接表为:"; for (auto& e : graph[i].adj) { cout << "(" << e.to << "," << e.weight << ") "; } cout << endl; } return 0; } ``` 上述代码中,我们先定义了邻接表存储结构中的边结构体 `Edge` 和顶点结构体 `Vertex`,然后通过 `createGraph` 函数创建无向邻接表存储结构,最后输出每个顶点的邻接表。其中,函数 `createGraph` 的输入参数包括中顶点个数 `n` 和边集合 `edges`,输出参数为无向邻接表存储结构 `vector<Vertex>`。在函数中,我们遍历边集合 `edges`,对于每条边 `(u, v, w)`,分别将 `(v, w)` 和 `(u, w)` 添加到顶点 `u` 和 `v` 的邻接表中,以此创建无向邻接表存储结构。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泳裤王子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值