LeetCode算法网站算法题
https://leetcode-cn.com/problems/course-schedule/
这道题的思路就是把课程表之间的关系转换为有向图,并检测有向图中是否有环
一.深度优先搜索
算法:
对于图中的每一个节点都有三个状态
(1)未搜索:我们还没有搜索到这个节点;
(2)搜索中:我们搜索过这个节点,但还没有回溯到该节点,即该节点还没有入栈,还有相邻的节点没有搜索完成);
(3)已完成:我们搜索过并且回溯过这个节点,即该节点已经入栈,并且所有该节点的相邻节点都出现在栈的更底部的位置,满足拓扑排序的要求。
通过上述的三种状态,我们就可以给出使用深度优先搜索得到拓扑排序的算法流程,在每一轮的搜索搜索开始时,我们任取一个「未搜索」的节点开始进行深度优先搜索。
我们将当前搜索的节点 u 标记为「搜索中」,遍历该节点的每一个相邻节点 v:
(1)如果 v 为「未搜索」,那么我们开始搜索 v,待搜索完成回溯到 u;
(2)如果 v 为「搜索中」,那么我们就找到了图中的一个环,因此是不存在拓扑排序的;
(3)如果 v 为「已完成」,那么说明 v 已经在栈中了,而 u 还不在栈中,因此 u 无论何时入栈都不会影响到 (u,v) 之前的拓扑关系,以及不用进行任何操作。
当 u 的所有相邻节点都为「已完成」时,我们将 u 放入栈中,并将其标记为「已完成」。
在整个深度优先搜索的过程结束后,如果我们没有找到图中的环,那么栈中存储这所有的 n 个节点,从栈顶到栈底的顺序即为一种拓扑排序。
最近需要学习Java的语法,所以使用Java来写题
class Solution {
private List<List<Integer>> pic;//图的邻接表数组
private int[] mark;//节点访问标记
private boolean flag=true;//判断有向图是否有环
public boolean canFinish(int numCourses, int[][] prerequisites) {
//创建邻接表的数组
pic=new ArrayList<List<Integer>>();
for (int i=0;i<numCourses;i++){
pic.add(new ArrayList<Integer>());
}
//设置遍历的标记数组
mark=new int[numCourses];
//绘制图
for(int[] info:prerequisites){
pic.get(info[1]).add(info[0]);
}
//遍历图做判断
for (int i=0;i<numCourses&&flag;i++){
if (mark[i]==0){//没有遍历过的点就继续遍历
dfs(i);
}
}
return flag;
}
private void dfs(int u){
mark[u]=1;
for (int v:pic.get(u)){
if (mark[v]==0){//没有遍历过的点就继续遍历
dfs(v);
if (!flag){//如果已经有环了直接终止遍历
return;
}
}else if (mark[v]==1){//如果当前节点已经遍历过,同时遍历它的邻接节点的操作并没有完成那么说明有环
System.out.println("错误"+u+v);
flag=false;
return;
}
}
//上面操作都完成了说明该节点遍历完成
mark[u]=2;
}
}
二.广度优先搜索
思路:
方法一的深度优先搜索是一种「逆向思维」:最先被放入栈中的节点是在拓扑排序中最后面的节点。我们也可以使用正向思维,顺序地生成拓扑排序,这种方法也更加直观。
我们考虑拓扑排序中最前面的节点,该节点一定不会有任何入边,也就是它没有任何的先修课程要求。当我们将一个节点加入答案中后,我们就可以移除它的所有出边,代表着它的相邻节点少了一门先修课程的要求。如果某个相邻节点变成了「没有任何入边的节点」,那么就代表着这门课可以开始学习了。按照这样的流程,我们不断地将没有入边的节点加入答案,直到答案中包含所有的节点(得到了一种拓扑排序)或者不存在没有入边的节点(图中包含环)。
上面的想法类似于广度优先搜索,因此我们可以将广度优先搜索的流程与拓扑排序的求解联系起来。
算法:
我们使用一个队列来进行广度优先搜索。初始时,所有入度为 0 的节点都被放入队列中,它们就是可以作为拓扑排序最前面的节点,并且它们之间的相对顺序是无关紧要的。
在广度优先搜索的每一步中,我们取出队首的节点 u:
(1)我们将 u 放入答案中;
(2)我们移除 u 的所有出边,也就是将 u 的所有相邻节点的入度减少 1。如果某个相邻节点 v 的入度变为 0,那么我们就将 v 放入队列中。
在广度优先搜索的过程结束后。如果答案中包含了这 n 个节点,那么我们就找到了一种拓扑排序,否则说明图中存在环,也就不存在拓扑排序了。
下面是我直接扒下来的C++代码,有时间再想
class Solution {
private:
vector<vector<int>> edges;
vector<int> indeg;
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
edges.resize(numCourses);
indeg.resize(numCourses);
for (const auto& info: prerequisites) {
edges[info[1]].push_back(info[0]);
++indeg[info[0]];
}
queue<int> q;
for (int i = 0; i < numCourses; ++i) {
if (indeg[i] == 0) {
q.push(i);
}
}
int visited = 0;
while (!q.empty()) {
++visited;
int u = q.front();
q.pop();
for (int v: edges[u]) {
--indeg[v];
if (indeg[v] == 0) {
q.push(v);
}
}
}
return visited == numCourses;
}
};