一、图的基础概念
1. 什么是图
由顶点(节点)和边组成的数据结构,用来描述多元素之间复杂关系。
- 顶点:图中的数据节点
- 边:两个顶点之间的连线
2. 图的分类
- 无向图:边没有方向,A 连通 B = B 连通 A
- 有向图:边有方向,单向通行
- 有权图:边上带有数值(距离、代价、流量)
- 无权图:边只代表连通关系,无数值
3. 基础术语
- 度:一个顶点相连边的数量
- 入度:有向图指向该点的边数
- 出度:有向图从该点出发的边数
- 连通图:任意两点之间都有路径可达
二、存储方式一:邻接矩阵
原理
用二维数组存储图关系
g[i][j] = 1:i 到 j 连通g[i][j] = 0:i 到 j 不连通- 有权图直接存入权重数值
1. 无向图邻接矩阵
const int N = 105;
int g[N][N];
// 初始化全部不连通
void init()
{
for(int i = 0; i < N; i++)
for(int j = 0; j < N; j++)
g[i][j] = 0;
}
无向图特点:矩阵沿对角线对称
2. 有向图邻接矩阵
只单向赋值,矩阵不对称。
邻接矩阵优缺点
- 优点:判断两点是否连通O(1),写法简单
- 缺点:空间复杂度高 O (n²),顶点数量多时极度浪费内存
- 适用:顶点数量少、稠密图
三、存储方式二:邻接表(刷题最常用)
原理
每个顶点开辟一条链表 /vector,存储它所有能直达的邻接点。C++ 刷题统一用 vector<vector<int>> 实现。
1. 无向图邻接表构建
#include <iostream>
#include <vector>
using namespace std;
const int N = 105;
vector<int> adj[N];
// 添加无向边
void addEdge(int u, int v)
{
adj[u].push_back(v);
adj[v].push_back(u);
}
2. 有向图邻接表构建
// 添加有向边 u -> v
void addDirEdge(int u, int v)
{
adj[u].push_back(v);
}
遍历邻接表
// 遍历u所有邻接点
for(int x : adj[u])
{
cout << x << " ";
}
邻接表优缺点
- 优点:空间极小,只存真实存在的边,稀疏图首选
- 缺点:判断两点连通需要遍历链表,速度慢一点
- 适用:顶点多、边少的稀疏图(算法题 90% 都是)
四、完整建图演示
int main()
{
// 构建无向图
addEdge(1,2);
addEdge(1,3);
addEdge(2,4);
addEdge(3,4);
// 输出1号节点所有邻居
cout << "顶点1连通节点:";
for(int x : adj[1]) cout << x << " ";
return 0;
}
五、两种存储方式选型口诀
- 顶点少、边密集 → 邻接矩阵
- 顶点多、边稀疏、算法刷题 → 邻接表 (vector)
- 判断两点快速连通用矩阵,遍历路径全用邻接表
六、图论学习路线预告
- 图存储(今日)
- DFS 深度优先遍历
- BFS 广度优先遍历
- 最短路径:Dijkstra、Floyd
- 最小生成树、拓扑排序
七、新手易错点
- 无向图建边只写单向,忘记双向添加
- 邻接矩阵数组开太小越界
- 混淆有向图与无向图建图逻辑
- 分不清稠密图与稀疏图该用哪种存储
八、今日总结
- 图分为有向 / 无向、有权 / 无权四大类
- 邻接矩阵:二维数组,简单耗空间
- 邻接表:vector 链表,刷题主流首选
- 建图是所有图论算法的前置基础