有向图是否有环、拓扑排序题
该题的本质是检查有向图是否有环,只要无环肯定能上完课程
【两种思路】
BFS思路:先计算邻接表、入度矩阵,然后用一个queue进行拓扑排序
DFS思路:先计算邻接表,用dfs判断每一个节点出发是否有环,用Boolean类型的visited矩阵记录是否访问、是否有环的情况
和802. Find Eventual Safe States [Medium]几乎一模一样https://blog.csdn.net/Squirrelmi/article/details/116307963,只是802直接给出了邻接表,本题是给出有向边,还需要自己建立邻接表
【BFS vs DFS】
都需要建立邻接表
BFS要建立入度矩阵int[] indegrees,要用queue
DFS要建立访问矩阵Boolean[] visited,要递归
/**
* BFS拓扑排序:
* 先建立和计算邻接表、入度矩阵,然后用一个queue进行拓扑排序
* 每删除一个队列中的节点,将numCourses--,并将与其临接的所有节点入度减一,将入度变为0的节点加入队列
* 队列为空时循环结束,numCourses==0说明能上完所有课,否则不能
* Runtime: 3 ms, faster than 85.93%
* Memory Usage: 39.8 MB, less than 38.80%
*/
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
// 拓扑排序准备工作:建立和计算临接表、入度矩阵
List<Integer>[] adjacency = new List[numCourses]; // 邻接表
int[] indegrees = new int[numCourses]; // 入度矩阵
for (int i = 0; i < numCourses; i++) // 为邻接表的每一行开辟空间
adjacency[i] = new ArrayList<>();
for (int[] pair : prerequisites) { // 计算邻接表和入度矩阵
indegrees[pair[0]]++;
adjacency[pair[1]].add(pair[0]);
}
// 开始拓扑排序
Deque<Integer> q = new LinkedList<>(); // 用于拓扑排序的队列
for (int i = 0; i < numCourses; i++) {
if (indegrees[i] == 0) {
q.offer(i);
}
}
while (!q.isEmpty()) {
int curr = q.poll();
numCourses--;
for (int i : adjacency[curr]) {
if (--indegrees[i] == 0) {
q.offer(i);
}
}
}
return numCourses == 0;
}
}
/**
* DFS思路,检查无环
* Runtime: 2 ms, faster than 99.01%
* Memory Usage: 39.4 MB, less than 65.06%
*/
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
// 计算邻接表
List<Integer>[] adjacency = new List[numCourses];
for (int i = 0; i < numCourses; i++) {
adjacency[i] = new ArrayList<>();
}
for (int[] pair : prerequisites) {
adjacency[pair[1]].add(pair[0]);
}
// 开始DFS判断无环
Boolean[] visited = new Boolean[numCourses];
for (int i = 0; i < numCourses; i++) {
if (!helper(adjacency, i, visited)) {
return false;
}
}
return true;
}
private boolean helper(List<Integer>[] adjacency, int i, Boolean[] visited) { // 检查是否没有环
if (visited[i] != null) {
return visited[i];
}
visited[i] = false;
for (int next : adjacency[i]) {
if (!helper(adjacency, next, visited)) {
return false;
}
}
visited[i] = true;
return true;
}
}
第一次复习
还是用了DFS和BFS两种方法,DFS写的和之前一模一样,BFS这次多一点注释可能好理解一些:
/**
* BFS(拓扑排序)
* 构造临接表和入度矩阵,并用一个queue进行拓扑排序
* 每次把当前能上的课(入度为0,即所有前置条件都达到了的)都上完,并解除它们后置节点的前置条件(入度--)
*/
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
if (prerequisites.length == 0 || prerequisites[0].length == 0) {
return true;
}
List<Integer>[] adjacency = new List[numCourses];
int[] indegree = new int[numCourses];
LinkedList<Integer> queue = new LinkedList();
// initiate adjacency table, indegree array, and queue
for (int i = 0; i < numCourses; i++) {
adjacency[i] = new LinkedList();
}
for (int[] pair : prerequisites) {
adjacency[pair[0]].add(pair[1]);
indegree[pair[1]]++;
}
for (int i = 0; i < numCourses; i++) {
if (indegree[i] == 0) {
queue.offer(i);
}
}
// topological sort
while (!queue.isEmpty()) {
int curr = queue.poll(); // finish the course numbered "curr"
numCourses--;
for (int next : adjacency[curr]) {
if (--indegree[next] == 0) {
queue.offer(next);
}
}
}
return numCourses == 0; // now we have finished all courses that can be finished, if there are still unfinished courses, the result is false
}
}
本文探讨了如何通过BFS(广度优先搜索)和DFS(深度优先搜索)算法判断有向图是否存在环,并利用这两种方法实现拓扑排序。两种方法对比,分别涉及邻接表构建、入度矩阵和访问矩阵的使用,以及在竞赛题目802.FindEventualSafeStates中的应用。
304

被折叠的 条评论
为什么被折叠?



