检测循环依赖
循环依赖检测。 如
[['A', 'B'], ['B', 'C'], ['C', 'D'], ['B', 'D']] => false,[['A', 'B'], ['B', 'C'], ['C', 'A']] => true
或者
小王写了一个makefile,其中有n个编译项编号为0~n-1,他们互相之间有依赖关系。请写一个程序解析依赖,给出一个可行的编译顺序。
拓扑排序
遍历输入数组以构建图的邻接表,入度数组。只存入度为0的节点的队列,结果链表存队列弹出的节点。从队列中取出一个节点, 将该节点添加到结果列表,遍历该节点的邻接节点,并减少它们的入度,如果邻接节点的入度变为0,则加入队列。
类似BFS。
public class Solution {
public List<Integer> haveCircularDependency(int n, int[][] prerequisites) {
List<List<Integer>> g = new ArrayList<>(); // 用于表示图的邻接表
int[] indeg = new int[n]; // 用于记录每个节点的入度
List<Integer> res = new ArrayList<>(); // 用于存储拓扑排序的结果
// 初始化邻接表
for (int i = 0; i < n; i++) {
g.add(new ArrayList<>());
}
// 构建图的邻接表并更新入度数组
for (int[] prerequisite : prerequisites) {
int a = prerequisite[0], b = prerequisite[1];
g.get(a).add(b); // 在邻接表中添加边
indeg[b]++; // 增加节点b的入度
}
// 创建队列用于拓扑排序。队列和res都是存储入度为0的节点,队列只是过渡。
Queue<Integer> q = new LinkedList<>();
// 将所有入度为0的节点加入队列
for (int i = 0; i < n; i++) {
if (indeg[i] == 0) {
q.offer(i);
}
}
// 进行拓扑排序
while (!q.isEmpty()) {
int t = q.poll(); // 从队列中取出一个节点。有出队就要考虑入队,循环流动。
res.add(t); // 将该节点添加到结果列表。注意返回的是代表操作顺序的列表,不是true或false。
// 出完队,要减少邻接节点的入度。
// 遍历该节点的邻接节点,并减少它们的入度
for (int j : g.get(t)) {
indeg[j]--; // 减少邻接节点的入度
if (indeg[j] == 0) {
q.offer(j); // 如果邻接节点的入度变为0,则加入队列
}
}
}
// 检查是否所有节点都在拓扑排序结果中
if (res.size() == n) {
return res; // 如果是,返回拓扑排序结果
} else {
return new ArrayList<>(); // 如果不是,说明存在循环依赖,返回空列表
}
}
}