题目:Course Schedule
给定了n各课程[0,n-1]和课程之间的依赖关系,课程i必须先完成课程j,即:课程i依赖于课程j。判断这些课程能否修完。
思路:
这些课程学习过程类似于拓扑排序,终点是要判断课程学习顺序(依赖关系)中是否有环,如果有环,则不能修完全部课程。
课程可以看成点,课程之间的依赖关系可以看成有向边,所以可通过广度优先搜索和深度优先搜索来判断是否有环。
下面我是通过深度优先搜索来实现的。
注意:
courses.at(p.first).clear();//课程p.first必定无环,所以后面都不用检测它,将其删除
这句话能够大幅度简化循环的次数,提高效率。因为前面检测一边没有环,则后面检测到该位置的课程时,就可以得出无环的结论。
bool LeetCode::canFinish(int numCourses, vector<pair<int, int>>& prerequisites){ if (numCourses < 1)return true;//没有课程 if (!prerequisites.size())return true;//没有依赖关系 vector<vector<int>>courses(numCourses);//第i个课程依赖于courses[i]里的课程 for (size_t i = 0; i < prerequisites.size(); i++){ courses.at(prerequisites.at(i).first).push_back(prerequisites.at(i).second); } vector<bool>visited(numCourses,false);//标记栈中存在的课程 for (size_t i = 0; i < numCourses; i++){ for (size_t j = 0; j < courses.at(i).size(); j++){ stack<pair<int, int>>s; s.push(make_pair(i,j));//课程i的依赖数组的第j个位置 visited.at(i) = true; while (!s.empty()){ auto p = s.top(); int next = courses.at(p.first).at(p.second);//下一个课程的下标 if (courses.at(next).size()){//下一个课程是否依赖其他课程 if (visited.at(next))return false;//依赖的课程是否已经在栈中,则存在环 s.push(make_pair(next, 0));//入栈 visited.at(next) = true;//标记 } else{ while (p.second + 1 >= courses.at(p.first).size()){//课程p.first全部遍历完,无环 visited.at(p.first) = false;//不标记 s.pop(); if (s.empty())break; courses.at(p.first).clear();//课程p.first必定无环,所以后面都不用检测它,将其删除 p = s.top(); } if (s.empty())break;//栈空 s.pop(); s.push(make_pair(p.first, p.second + 1));//课程p.first的下一个依赖课程 } } } //课程i的所有依赖都不构成环。 if (courses.at(i).size()) courses.at(i).clear(); } return true; }
题目:Course ScheduleII
题目的意思和上面的类似,但是下面的需要返回学习的顺序。
思路:
同样按照上面的分析,只需要判断是否有环。同样可以使用广度优先搜索或深度优先搜索。
这里通过求出每个点的入度判断每个课程的顺序。
vector<int> LeetCode::findOrder(int numCourses, vector<pair<int, int>>& prerequisites){ vector<int>path;//记录合法的路径 if (numCourses < 1)return path;//没有课程 vector<vector<int>>courses(numCourses);//第i个课程被courses[i]里的课程依赖 vector<int>indegrees(numCourses,0);//记录每个点的入度 for each (auto p in prerequisites){ courses.at(p.second).push_back(p.first); ++indegrees.at(p.first);//计算依赖关系的入度 } queue<int>Q;//先统计入度为0的点 for (size_t i = 0; i < numCourses; i++){ if (!indegrees.at(i))Q.push(i); } for (size_t i = 0; i < numCourses; i++){ if (!Q.size()){ if (path.size() < numCourses)path.clear();//有环 return path; } int z = Q.front(); Q.pop(); path.push_back(z);//记录拓扑排序的路径 for each (auto p in courses.at(z)){ --indegrees.at(p);//入度减一 if (!indegrees.at(p))Q.push(p);//入度减为零时,表示会有其他依赖关系,则可以添加进队列中 } } return path; }