【算法基础】图的存储与遍历

一、图的存储

在我们存储图的时候,主要使用邻接矩阵、邻接表两种方式来存储。通常邻接矩阵存储稠密图(边多),临界矩阵存储稀疏图(边少)。

1.1 邻接矩阵存储

邻接矩阵听起来比较高大上,其实就是用二维数组来表示 a 点与 b 点之间有一条边。例如在上述无向图中 14 之间有一条无向边。用邻接矩阵来存储的话就是

g[1][4]=19g[4][1]=19

表示点 1 到点 4 之间的权重是 19 ,因为是无向边所以 4 也可以到 9 权重是 19 。如果是有向图,建边的时候仅能根据方向建边

g[1][4]=19

可见邻接矩阵存储非常 ez 但是由于内存的限制,点也肯定不会太多。如果点很多 (104) 就仅能用邻接表存储了。

根据以上原理,我们上述无向图的存储结果便是:

1.2 邻接表存储

邻接表也一样,听起来非常高大上,其实相当简单。他就是用链表的思想。如下图所示

例如:点 1 可以到达的点有 {2,3,4} ,点 4 可以到达的点有 {1,2,3,5,7,9} 以此不断类推。关于边权,我们也仅需再开一个数组同时来维护即可。

二、图的存储代码实现

如果我们有如下读入数据

5 6
1 2 2
2 3 1
1 3 4
2 5 3
3 5 1
4 2 4

第一行包含整数 nm。代表给定一个 n 个点 m 条边的有向图

接下来 m 行每行包含三个整数 x,y,z ,表示存在一条从点 x 到点 y 的有向边,边长为 z

2.1 邻接矩阵存储实现

const int N = 1010;
int g[N][N];
int main()
{
    int n, m;
    std::cin >> n >> m;
    memset(g, 0x3f, sizeof g);
    for(int i = 1; i <= m; i++)
    {
        int x, y, z;
        std::cin >> x >> y >> z;
        g[x][y] = z;	// 如果有重边则根据题意保留小边或大边
        // g[y][x] = z;	// 如果是无向图,点 y 到点 x 也需要建边
    }
}
N = int(1e3 + 10)
INT_MAX = int(2e10)
g = [[INT_MAX for i in range(N)] for j in range(N)]
n, m = map(int, input().split())
for t in range(m):
    x, y, z = map(int, input().split())
    g[x][y] = z 	# 如果有重边则根据题意保留小边或大边
  # g[y][x] = z;	  如果是无向图,点 y 到点 x 也需要建边

2.2 邻接表利用 vector 实现

int main()
{
    int n, m;
	std::cin >> n >> m;

	std::vector<int> e[n + 1], w[n + 1];	// 注意这里是中括号
	for(int i = 1; i <= m; i++)
	{
		int x, y, z;
		std::cin >> x >> y >> z;
		e[x].push_back(y);		// 邻接表无法处理重边,直接存就好,后面有最短路的问题,再在后面做处理即可
		w[x].push_back(z);
        // 如果是无向图也需要添加, e[y].push_back(x);	w[y].push_back(z);
	}
}
n, m = map(int, input().split())
e = [[] for i in range(n + 1)]
w = [[] for i in range(n + 1)]  # 或 w = e[:] 这里一定要切片
for i in range(m):
    x, y, z = map(int, input().split())
    e[x].append(y)
    w[x].append(z)
   # 如果是无向图也需要添加, e[y].append(x);	w[y].append(z);

2.3 邻接表用数组实现(静态的)【链式前向星】

const int N = 1e5 + 10, M = 5e5 + 10;
int h[N], ne[M], e[M], w[M], idx;
int d[N];
int n, m;

void add(int a, int b, int c){
	ne[idx] = h[a], e[idx] = b, w[idx] = c, h[a] = idx++;
}

int main()
{
    memset(h, -1, sizeof h);	// 切记这里的头链表一定要初始化成 -1
	std::cin >> n >> m;
	for(int i = 1; i <= m; i++)
	{
		int a, b, c;
		std::cin >> a >> b >> c;
		add(a, b, c); // 同理,如果无向图需要 add(b, a, c);
	}
}
n, m = map(int, input().split())
INF = 1e12
N = 1000000 + 10
h, e, w, ne = [-1] * N, [0] * N, [0] * N, [0] * N
st = [False] * N

for i in range(m):
    a, b, c = map(int, input().split())
    add(a, b, c)
   	# 同理无向图需要 add(b, a, c)

三、图的遍历代码实现

关于图和树的遍历都是用 dfsbfs 进行遍历的。

3.1 邻接矩阵遍历

bool st[N];
void dfs(int u)
{
    st[u] = true;
    for(int i = 1; i <= n; i++)
    {
        if(g[u][i] != 0 && !st[i]) dfs(i);	// 只要点 u 可以达到点 i ,且点 i 没有被访问过,进行遍历即可。
        // 具体的遍历法制需要根据题意进行修改
	}
}
st = [False] * (n + 1)
def dfs(u):
    st[u] = True
    for i in range(1, n + 1):
        if g[u][i] != 0 and not st[i]: dfs(i)

3.2 邻接表遍历 (vector)

bool st[N];
void dfs(int u)
{
	st[u] = true;
	for(int i = 0; i < e[u].size(); i++)
	{
		if(!st[e[u][i]]) dfs(e[u][i]);  // 只要点 u 可以达到点 i ,且点 i 没有被访问过,进行遍历即可。
	}
}
st = [False] * (n + 1)
def dfs(u):
    st[u] = True
    for i in range(len(e[u])):
        if st[e[u][i]]: dfs(e[u][i])

3.3 邻接表遍历(链式前向星)

bool st[N];
void dfs(int u) {
    if(st[u]) return;
    st[u] = true;
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];	// 这个点去哪里
        dfs(j);
	}
}
st = [False] * (n + 1)
def dfs(u):
    st[u] = True
    i = h[u]
    while i != -1:
        j = e[i]
        dfs(j)
        i = ne[i]
posted @   wxzcch  阅读(140)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
历史上的今天:
2023-12-12 【Pytorch基础实战】第一节,BP神经网络
点击右上角即可分享
微信分享提示