题目
207. 课程表
我的思路
经过分析,本题似乎等价于判断一个图中是否存在环。我的朴素思路是,先把图用数据结构表示起来,然后依次对每个节点深搜,当路径长于图中的节点数时,说明存在环。
在实现上有许多地方可以改进,值得推敲。首先我在实现时是用链表表示有向图的,可以用更适合图(而非树)的邻接表来表示。其次我在深搜时,没有进行记忆化处理。最后,在判断是否存在环时用路径长度作为判断标准效率也很低。
下面整理一下官方题解的思路。
首先本题等价于判断是否存在“拓扑排序”问题。而由拓扑排序相关算法的性质,等价于寻找一个“拓扑排序”的问题。这类问题是有套路和模板的。
不论深搜还是广搜,核心思路是不断地寻找并访问入度为0的节点(安全节点)。
思路一:深搜
所有的节点有三种状态,未访问,访问过,和正在访问。当搜索到正在访问的节点时说明有环。
递归思路:把当前访问的节点设置为正在访问状态,遍历当前节点指向的所有节点,若遍历完毕,把当前节点设为已访问。尽管此题目不需要,但是可以在标记节点为已访问时,把该节点入栈,这个栈可以作为一种拓扑排序!
class Solution { private: vector<vector<int>> edges; vector<int> visited; bool valid = true; public: void dfs(int u) { visited[u] = 1; for (int v: edges[u]) { if (visited[v] == 0) { dfs(v); if (!valid) { return; } } else if (visited[v] == 1) { valid = false; return; } } visited[u] = 2; } bool canFinish(int numCourses, vector<vector<int>>& prerequisites) { edges.resize(numCourses); visited.resize(numCourses); for (const auto& info: prerequisites) { edges[info[1]].push_back(info[0]); } for (int i = 0; i < numCourses && valid; ++i) { if (!visited[i]) { dfs(i); } } return valid; } }; 作者:LeetCode-Solution 链接:https://leetcode-cn.com/problems/course-schedule/solution/ke-cheng-biao-by-leetcode-solution/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
思路二:广搜(非递归)
广搜需要从入度为0,也就是拓扑排序中可以可以直接作为首元素的节点开始搜索。用一个辅助数组记录所有节点的入度数。
循环:把入度为0的节点加入队列,取队首,消除与队首有关的边(把辅助数组中相关节点的入度数-1)。取队首时可以把它加入结果队列,最后形成一个“拓扑排序”。出循环的条件是直到队列为空,比较结果队列的长度和节点数即可得知是否存在“拓扑排序”。
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; } }; 作者:LeetCode-Solution 链接:https://leetcode-cn.com/problems/course-schedule/solution/ke-cheng-biao-by-leetcode-solution/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我的实现
class Solution { public: struct TreeNode{ int val; vector<TreeNode*> Children; TreeNode(int v):val(v),Children(){}; }; bool search(TreeNode *presentRoot,int count,const int & max){ if(count>max){ return false; } for(auto it: presentRoot->Children) { if(false==search(it,count+1,max))return false; } return true; } bool canFinish(int numCourses, vector<vector<int>>& prerequisites) { vector<TreeNode*> allcourses; TreeNode *temp; for(int i=0;i<numCourses;i++) { temp = new TreeNode(i); allcourses.push_back(temp); } for(auto it: prerequisites) { //cout<<it[0]<<it[1]<<endl; (allcourses[it[0]]->Children).push_back(allcourses[it[1]]); } for(auto it: allcourses) { if(search(it,0,numCourses)==false)return false; } return true; } }; /* 好像就是等效于检测是否存在环(a,b) (b..,..z) (z,a) 深搜? struct TreeNode{ int val; vector<TreeNode*> Children; TreeNode(v):val(v),Children():{}; } 1.为prerequisites中出现的匹配建造一些树 2.深搜每一个非叶子节点,若出现环,那么返回false;如果没有环,那么true */
拓展学习
拓扑排序
邻接表