概述
- 一个由顶点和边组成的非线性数据结构,突出点与点之间的关系。广泛应用与现实场景的各种抽象建模,很灵活
- 由图的建模使用可以衍生出各种各样的算法,这里不做详细说明,需要的时候在补上
简单图示

特性
相对详细内容请查看 图论
在图论介绍的各种分类以及性质,最终在数据结构层面影响到的是数据的存储、表示方式
表示方式
以图示的图为例,表示方式如下
-
邻接矩阵
以一个二维矩阵表示点与点之间的关系。如图所示,矩阵下标表示点的编号,矩阵值表示点与点是否相连(通常0表示没有相连,1及以上表示连接/边的权值)(图中省略0)

-
邻接表
以一个链表集合表示点与点之间关系。如图所示,集合中每个元素表示一个点,每个点有一个链表表示该点与其它点的关系

-
直接存边
使用一个数组直接存边,每个元素包含起点和终点,若有边权则额外补充
复杂度分析
假设图的点数为n,边数为m
邻接矩阵
| 操作 |
时间复杂度 |
空间复杂度 |
| 构建 |
- |
O(n^2) |
| 查边 |
O(1) |
- |
| 遍历点所有出边 |
O(n) |
- |
| 遍历图 |
O(n^2) |
- |
邻接表
| 操作 |
时间复杂度 |
空间复杂度 |
| 构建 |
- |
O(m) |
| 查边 |
O(k)(k为点的出度/链表长度) |
- |
| 遍历点所有出边 |
O(k) |
- |
| 遍历图 |
O(n + m) |
- |
边集
| 操作 |
时间复杂度 |
空间复杂度 |
| 构建 |
- |
O(m) |
| 查边 |
O(m) |
- |
| 遍历点所有出边 |
O(m) |
- |
| 遍历图 |
O(nm) |
- |
衍生部分算法
遍历
BFS (广度优先搜索)
DFS (深度优先搜试)
- 使用递归、栈(迭代)完成分支深度搜索
- 适用于搜索所有路径等
最短路径
最小生成树
拓扑排序
二分图匹配
网络流
着色算法
发散/随想/自身理解(?)
- 用处太广泛了,很多问题场景都可以用图来构建,而且衍生的算法太多了,很复杂
- 不过在我的工作场景中好像没咋涉及到图结构的构建,衍生算法应该是有用到一点,但没啥印象
- 刷题的时候遇到一大堆,不会的也一大堆
实现(C++)
构建
1 2 3 4 5
| void Build(int n, vector<vector<int>> edges) {
}
|
存边
1 2 3 4 5 6 7 8 9 10 11
| struct Edge { int start; int end; int weight; };
vector<Edge> graph;
for (auto& edge: edges) { graph.push_back({edge[0], edge[1], edge[2]}); }
|
邻接矩阵
1 2 3 4 5 6 7
| vector<vector<int>> graph(n, vector<int>(n, 0));
for (auto& edge: edges) { graph[edge[0]][edge[1]] = edge[2]; }
|
邻接表
1 2 3 4 5 6 7 8 9 10 11 12
| struct Info { int p; int w; };
vector<vector<Info>> graph(n);
for (auto& edge: edges) { graph[edge[0]].push_back({edge[1], edge[2]}); }
|
遍历
DFS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| vector<vector<int>> g;
void dfs_recursion(int v) { if (v >= g.size()) return;
vector<bool> visited(g.size(), false); function<void(int)> dfs = [&](int x) { if (visited[x]) return;
visited[x] = true; for (auto y: g[x]) { if (visited[y]) continue; dfs(y); } };
dfs(v); }
void dfs_iteration(int x) { if (x >= g.size()) return; vector<bool> visited(g.size(), false); stack<int> st;
st.push(x);
while (!st.empty()) { int top = st.top(); st.pop(); if (visited[top]) continue; if (top >= g.size()) continue;
visited[top] = true; for (int i = g[top].size() - 1; i >= 0; i--) { int y = g[top][i]; if (visited[y]) continue; st.push(y); } }
}
|
BFS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| void bfs(int x) { if (x >= g.size()) return; vector<bool> visited(g.size(), false); queue<int> que;
que.push(x);
while (!que.empty()) { int front = que.front(); que.pop(); if (visited[front]) continue; if (front >= g.size()) continue;
visited[front] = true; for (auto y: g[front]) { if (y >= g.size() || visited[y]) continue; que.push(y); } }
}
|