拓扑排序详解及运用 :: labuladong的算法小抄 #984
Replies: 77 comments 21 replies
-
visited 和 onPath 这两个变量是否有些重复? |
Beta Was this translation helpful? Give feedback.
-
不重复,这两个变量很有必要,目的不一样。visited可以减少很大一部分计算量 |
Beta Was this translation helpful? Give feedback.
-
一开始我也在想visited是否重复,但是正如前者说的那样,这么做会减少计算量,如果碰到visited被标记为true,则说明前面已经对这个节点之后的样子做出判断了,无须再进行延伸,所以减少计算量 |
Beta Was this translation helpful? Give feedback.
-
课程表那道题,traverse中的两个if不能调换,哈哈,错这了 |
Beta Was this translation helpful? Give feedback.
-
visited是以整个图为维度判断是否重复遍历,onPath是以路径为维度判断是否有环,区别在于后续遍历的处理。两者不能互相替代。 |
Beta Was this translation helpful? Give feedback.
-
@Inn0vati0n @hui60 @0xlightyear 对的, 类比贪吃蛇游戏, |
Beta Was this translation helpful? Give feedback.
-
我认为还是有些重复的,可以使用染色法,将visited数组定义为int型,visited[0]表示未遍历,visited[1]表示遍历栈中,visited[2]表示已遍历完成,这样就能丢弃onPath数组 |
Beta Was this translation helpful? Give feedback.
-
讲道理,BFS版的拓扑还是更容易点更适合面试。。。 |
Beta Was this translation helpful? Give feedback.
-
@labuladong [不需要翻转]
// 逆后序遍历结果即为拓扑排序结果
Collections.reverse(postorder); |
Beta Was this translation helpful? Give feedback.
-
需要反转,看图就能明白。不过面试碰上还是BFS比较清楚 https://zhuanlan.zhihu.com/p/135094687 。。。递归的话确实只有后序遍历,先处理两个子节点再处理父节点这种能表达出依赖关系,前序(根左右)和中序(左根右)都不行。 |
Beta Was this translation helpful? Give feedback.
-
@z-ak-z @553269487 按照我的解法代码逻辑是需要反转的,不过你确实可能看到有的解法代码没有对后序遍历结果进行翻转,是因为那种解法建图的时候对边的定义和我不同,我解法建的图中箭头方向是「被依赖」关系,如果你定义为「依赖」关系,整幅图中边全部反转,那就可以不对后序遍历结果反转。 具体来说,你把我的代码中 |
Beta Was this translation helpful? Give feedback.
-
@labuladong int from = edge[1];
int to = edge[0]; |
Beta Was this translation helpful? Give feedback.
-
visit数组和onpath是不冲突的,一开始也想不明白。“明明visit【s】=true就说明已经来过了呀,那肯定成环了”,后来想了个菱形有向图如下,原来1-2-4 这次遍历中visit【2】=true,但在1-3-2-4这轮遍历的时候,visit【2】不表示有环。也就是说只有在本轮遍历中出现visit【s】=true才存在环,而本轮遍历要靠onpath来判断。 也就是@Inn0vati0n 说的 |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
感觉思路上首先想到的应该是前序遍历,这里采用后序再反转的原因是,主函数开始时要把所有点作为根节点弄一遍,前序会出问题。 |
Beta Was this translation helpful? Give feedback.
-
@labuladong,我的个人理解是visit[]只是起到一个剪枝的作用,只是为了避免重复计算,onPath只是是记录路径+环检测的作用; |
Beta Was this translation helpful? Give feedback.
-
还是 BFS 好理解一点 |
Beta Was this translation helpful? Give feedback.
-
好厉害 把这两道题好好研究了一下 收获很大 谢谢作者 |
Beta Was this translation helpful? Give feedback.
-
东哥,今天看完你的拓扑排序之后有一些不太一样的感悟,我想分享一下,之前 有一篇文章里面讲关于 DFS和backtracking回溯的区分,dfs更侧重在 节点上进行处理, 而backtrack则是在 树枝上进行处理,这篇文章上 拓扑排序使用 dfs的部分,虽然说是回溯,但感觉更偏向 dfs那种。因为对数据的处理主要还是在节点上完成的。如果是在树枝上完成,处理的部分应该是在for循环内部。分享完毕,欢迎大家指正! |
Beta Was this translation helpful? Give feedback.
-
感觉还是不要反过来更好懂,边的指向是前提,所以用后续遍历就可以学完前提再学根节点的课程,不然要绕一下 |
Beta Was this translation helpful? Give feedback.
-
LinkedList的addFirst方法可以避免reverse postorder. |
Beta Was this translation helpful? Give feedback.
-
看了后序遍历的拓扑排序,当时想到能不能用前序遍历来做,是不是前序遍历然后不用倒序然后组合下就可以了,结论是行不通的,但是leetcode的用例居然只有一个失败。。 |
Beta Was this translation helpful? Give feedback.
-
leetcode上用DFS如果超时可以尝试在for循环那里加一个判定 |
Beta Was this translation helpful? Give feedback.
-
寻找带有环的图中的环的所有节点: public class Solution {
boolean hasCycle = false;
boolean[] visited;
boolean[] onPath;
Integer cycleNode = null;
int[] findCycleNodes(int numCourses, int[][] prerequisites) {
boolean canFinish = canFinishWithCycleNodes(numCourses, prerequisites);
if (canFinish || cycleNode == null) { // 没有环或者没有找到环的入口点
return new int[] {};
}
// 路径中的所有节点并非环中的所有节点,取环中的所有节点
int cycleStartIndex = -1;
for (int i=0;i < cyclePathNodeList.size();i++) {
// 都是在路径上的,且当前节点 i 为环的入口
if (cycleNode == cyclePathNodeList.get(i)) {
cycleStartIndex = i; // 首次找到入口
break;
}
}
if (cycleStartIndex == -1) {
return new int[] {};
}
// 最后一个节点就是最后的那个重复的入口点
return cyclePathNodeList.subList(cycleStartIndex, cyclePathNodeList.size() - 1).stream()
.mapToInt(x -> x.intValue()).toArray();
}
List<Integer> cyclePathNodeList = new ArrayList<>();
boolean canFinishWithCycleNodes(int numCourses, int[][] prerequisites) {
onPath = new boolean[numCourses];
visited = new boolean[numCourses];
List<Integer>[] graph = buildGraph(numCourses, prerequisites);
for (int i = 0; i < numCourses; i++) {
traverseWithCycle(graph, i);
}
return !hasCycle;
}
void traverseWithCycle(List<Integer>[] graph, int s) {
if (onPath[s]) {
hasCycle = true;
// 也可以在这个地方收集所有的 onPath 为 true 的节点
cycleNode = s;
cyclePathNodeList.add(s);
}
if (visited[s] || hasCycle) {
return;
}
visited[s] = true;
onPath[s] = true;
cyclePathNodeList.add(s);
for (int to : graph[s]) {
traverseWithCycle(graph, to);
}
// 后序位置
onPath[s] = false;
}
} |
Beta Was this translation helpful? Give feedback.
-
vector< vector> find(unordered_map<int, vector>& graph, int m) { |
Beta Was this translation helpful? Give feedback.
-
请问东哥和各位大佬,我把逻辑改成C语言这么写当输入位prerequisites =[[1,4],[2,4],[3,1],[3,2]] numCourses =5时 int *graph_list; //记录先修课程后面需要修的课程 //用矩阵方式建立邻接表 //遍历邻接表
} bool canFinish(int numCourses, int** prerequisites, int prerequisitesSize, int* prerequisitesColSize){ |
Beta Was this translation helpful? Give feedback.
-
class Solution {
private:
vector<bool> visited;
vector<bool> onPath;
bool hasCycle = false;
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
// construct a grpah
vector<vector<int>> graph = makeGraph(numCourses, prerequisites);
for (int i = 0; i < numCourses; i++) {
visited.push_back(false);
onPath.push_back(false);
}
for (int i = 0; i < numCourses; i++) {
traverse(graph, i);
if (hasCycle == true) {
return false;
}
}
return true;
}
// return a graph represented as a adj list
vector<vector<int>> makeGraph(int numCourses, vector<vector<int>>& prerequisites) {
vector<vector<int>> graph;
for (int i = 0; i < numCourses; i++) {
vector<int> adj;
graph.push_back(adj);
}
for (vector<int> p : prerequisites) {
graph[p[1]].push_back(p[0]);
}
return graph;
}
// DFS traverse
void traverse(vector<vector<int>> graph, int node) {
if (onPath[node]) {
hasCycle = true;
return;
}
if (visited[node] || hasCycle) {
return;
}
visited[node] = true;
onPath[node] = true;
vector<int> neighbours = graph[node];
for (int i = 0; i < neighbours.size(); i++) {
traverse(graph, neighbours[i]);
}
onPath[node] = false;
}
}; 力扣207题这个DFS解法一直TLE,感觉和东哥解法差不多,不知道为啥超时了,哪位大佬可以帮忙看一下多谢啦! |
Beta Was this translation helpful? Give feedback.
-
DAG的邊,可以當做課程節點間的單向關係(思路不知道正不正確) |
Beta Was this translation helpful? Give feedback.
-
这里判断是否有环的时候,如果有环,代码是不是还会继续执行遍历图的部分啊 |
Beta Was this translation helpful? Give feedback.
-
leetcode显示BFS解法慢了2ms,对OA通过有影响么,还是遇到类似的题目直接DFS来解? |
Beta Was this translation helpful? Give feedback.
-
文章链接点这里:拓扑排序详解及运用
评论礼仪 见这里,违者直接拉黑。
Beta Was this translation helpful? Give feedback.
All reactions