拓扑排序详解及运用 :: 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.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
文章链接点这里:拓扑排序详解及运用
评论礼仪 见这里,违者直接拉黑。
Beta Was this translation helpful? Give feedback.
All reactions