Solutions to high-frequency interview questions of LeetCode in C++17, taking into account both efficiency and comprehensibility.
有向无环图
a->b->c->d
↓ ↓
e->f
非有向无环图(bcfe 形成有向回路)
a->b->c->d
↑ ↓
e<-f
课程学习工程图的 AOV 网
a->b->c->d
↓ ↓
e->f
a:离散数学
b:数据结构
a->b 表示学习 b 之前需要先学习 a
a->b->c->d
↑ ↓
e<-f
学习 b 之前要学习 a 和 e,a 可以学习
学习 e 之前要学习 f
学习 f 之前要学习 c
学习 c 之前要学习 b
陷入死循环
a->b->c->d
↓ ↓
e->f
对于图中的任意一条边的两个顶点 xy,如果 x-> y,则线性序列中 x 出现在 y 之前
满足这一条件的线性序列即为拓扑序列,如 abcdef 就是一个拓扑序列
________
| ↓
a->b->c->d e->f
|________↑
从有向图构建拓扑序列的过程即为拓扑排序,其过程如下:
选择一个无前驱节点的点,添加到线性序列:
a
从图中删除该顶点,并删除所有从它出发的有向边:
b->c->d
↓ ↓
e->f
继续选择一个无前驱节点的点,添加到线性序列:
ab
从图中删除该顶点,并删除所有从它出发的有向边:
c->d
↓
e->f
继续选择一个无前驱节点的点(e 和 c 均可),添加到线性序列:
abe
从图中删除该顶点,并删除所有从它出发的有向边:
c->d
↓
f
继续选择一个无前驱节点的点,添加到线性序列:
abec
从图中删除该顶点,并删除所有从它出发的有向边:
d
f
重复上述步骤,直到从图中删去所有顶点:
abecdf
_____
| ↓
a->b->e c->d f
| |_____↑
|________|
由上述过程可知,拓扑序列并不唯一
a->b->c->d
↑ ↓
e<-f
选择一个无前驱节点的点,添加到线性序列:
a
从图中删除该顶点,并删除所有从它出发的有向边:
b->c->d
↑ ↓
e<-f
继续选择一个无前驱节点的点,添加到线性序列
此时无满足条件的点,因此构造结束,最后的序列为 a,不为拓扑序列
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
unordered_map<int, vector<int>> m; // 邻接表(节点及其所有后继节点)
vector<int> indegree(numCourses); // 节点的入度(以该点为后继节点的边数)
for (auto& x : prerequisites) {
m[x[1]].emplace_back(x[0]); // 题目中 x[1] 是前驱节点,x[1]->x[0]
++indegree[x[0]]; // x[0] 入度加 1
}
queue<int> q;
for (int i = 0; i < size(indegree); ++i) {
if (!indegree[i]) {
q.emplace(i); // 把入度为 0 的点添加到队列中
}
}
int cnt = 0;
while (!empty(q)) {
int n = q.front(); // 入度为 0 的点
q.pop(); // 取出该点
++cnt;
for (auto& x : m[n]) { // 遍历该点所有后继节点
--indegree[x]; // 因为删除了当前节点,后继节点入度减一
if (!indegree[x]) {
q.emplace(x); // 如果后继节点入度为 0 则推入队列中
}
}
}
return cnt == numCourses; // 遍历了所有点则说明是有向无环图
}
};