一、上期回顾 Day29
掌握 BFS 广度优先搜索,队列层序遍历,求解迷宫、二叉树层序、无权最短路径。今天进入图论入门,学习图存储与拓扑排序,解决有向图任务依赖问题。
二、图基础概念
- 顶点:图中的节点
- 边:节点之间的连接关系
- 有向图:边有方向,A→B 不等于 B→A
- 无向图:边无方向,双向连通
- 入度:指向当前节点的边数量
- 出度:从当前节点出发的边数量
三、图常用存储:邻接表
刷题首选邻接表,节省空间、遍历高效
#include <iostream>
#include <vector>
using namespace std;
const int N = 1005;
vector<int> edge[N]; // 邻接表
int inDegree[N]; // 记录每个点入度
四、拓扑排序核心思想
适用场景:有先后依赖关系的任务排序
- 不断选取入度为 0的节点输出
- 删除该节点所有出边,相邻节点入度 - 1
- 重复操作直到所有节点输出
- 最终输出节点数≠总节点数 → 图存在环,无法排序
五、拓扑排序 BFS 标准模板
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int N = 1005;
vector<int> edge[N];
int inDegree[N];
vector<int> res;
// n个顶点,m条边
bool topoSort(int n, int m)
{
queue<int> q;
// 入度为0节点入队
for(int i = 1; i <= n; i++)
{
if(inDegree[i] == 0)
q.push(i);
}
while(!q.empty())
{
int u = q.front();
q.pop();
res.push_back(u);
// 遍历所有邻接点
for(int v : edge[u])
{
inDegree[v]--;
if(inDegree[v] == 0)
q.push(v);
}
}
// 节点数量相等无环,否则有环
return res.size() == n;
}
int main()
{
int n, m;
cin >> n >> m;
for(int i = 0; i < m; i++)
{
int a, b;
cin >> a >> b;
edge[a].push_back(b);
inDegree[b]++;
}
bool ok = topoSort(n, m);
if(ok)
{
for(int x : res)
cout << x << " ";
}
else
cout << "图中存在环路,无法拓扑排序";
return 0;
}
六、拓扑排序典型场景
- 课程先后选修安排
- 项目任务依赖执行顺序
- 编译文件依赖排序
- 判断有向图是否存在环
七、拓扑排序关键点
- 依赖关系
A→B:A 先执行,B 后执行 - 每次只选无前置依赖(入度 0)节点
- 统计最终输出个数,快速判环
- 基于 BFS 队列实现,逻辑简单不易出错
八、今日核心总结
- 图存储优先使用邻接表
- 拓扑排序专门解决有向图依赖排序
- 核心:入度清零出队,递减邻接点入度
- 输出节点总数判断是否存在环路
- 队列 BFS 写法通用性最强
九、课后练习
- 搭建简单有向图,运行拓扑排序输出执行顺序
- 手动构造带环图,验证判环效果