有向无环图的拓扑排序
解题思路
numCourses 相当于节点的数目
prerequisites 描述了所有的有向边
如果有向图中存在环,根据每次只Push入度为0 的节点的规则,是不可能遍历所有节点的
环的意思就是存在相互依赖关系
精简的代码
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
std::unordered_map<int , std::unordered_set<int> > linkedTable;
std::vector<int > inDegree(numCourses,0);
for(int i=0;i<prerequisites.size();i++)
{
linkedTable[ prerequisites[i][1] ].emplace(prerequisites[i][0]);
inDegree[prerequisites[i][0]] ++ ;
}
std::deque<int> q;
for(int i=0;i<inDegree.size();i++)
{
if(inDegree[i]==0) q.push_back(i);
}
//start BFS
int cnt=0;
while(!q.empty() )
{
auto node= q.front(); q.pop_front();
cnt++;
for ( const auto & x: linkedTable[node])
{
inDegree[x] --;
if( inDegree[x]==0 ) q.push_back(x) ;
}
}
return cnt == numCourses;
}
};
调试用代码
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
//
// 邻接表 可以很方便找到 与当前节点连接的节点
// 入度为0 的点设置为BFS的起始点 向队列中添加新元素的规则是-1后 入度为0 的节点
std::unordered_map<int , std::unordered_set<int> > linkedTable;
std::vector<int > inDegree(numCourses,0);
// for(int i=0;i<numCourses;i++)
// {
// linkedTable.emplace(std::make_pair(i,std::unordered_set<int>() ) );
// }
for(int i=0;i<prerequisites.size();i++)
{
linkedTable[ prerequisites[i][1] ].emplace(prerequisites[i][0]);
inDegree[prerequisites[i][0]] ++ ;
}
//两个节点 一条边
// assert(linkedTable.size()==numCourses);
// assert(inDegree.size()==numCourses);
for(auto t: linkedTable)
{
std::cout << t.first <<" ";
for(auto x: t.second)
{
std::cout<< x << " ";
}
std::cout << std::endl;
}
// for(auto t: inDegree)
// {
// std::cout <<t.first <<" In= " <<t.second << std::endl;
// }
std::deque<int> q;
for(int i=0;i<inDegree.size();i++)
{
if(inDegree[i]==0) q.push_back(i);
}
//start BFS
int cnt=0;
while(!q.empty() )
{
auto node= q.front(); q.pop_front();
std::cout << node << " ";
cnt++;
for ( const auto & x: linkedTable[node])
{
inDegree[x] --; //这里只更新入度表 不从邻接表删除元素是可以的
if( inDegree[x]==0 ) q.push_back(x) ;
}
}
return cnt == numCourses;
//e.g 3 [[1,0],[0,1], [2,1] , [1,2]]
//is [0] <--> [1] <--> [2]
//邻接表
// for(auto t: linkedTable)
// {
// std::cout << t.first <<" ";
// for(auto x: t.second)
// {
// std::cout<< x << " ";
// }
// std::cout << std::endl;
// }
// // 2 1
// 1 2 0
// 0 1
//入度表
// for(auto t: inDegree)
// {
// std::cout <<t.first <<" In= " <<t.second << std::endl;
// }
// 2 In= 1
// 0 In= 1
// 1 In= 2
}
};
题目描述
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
示例 1:
输入:numCourses = 2, prerequisites = [[1,0]]
输出:true
解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。
示例 2:
输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false
解释:总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。
提示:
1 <= numCourses <= 105
0 <= prerequisites.length <= 5000
prerequisites[i].length == 2
0 <= ai, bi < numCourses
prerequisites[i] 中的所有课程对 互不相同
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/course-schedule
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
如果采用DFS呢?
解题思路
DFS 思路 相当于以每个节点作为起始点都进行深度优先搜索
如果在递归过程中遇到一件访问的 (flag=2),说明 有环存在 ,应该及时剪枝 退出循环
因为这里的DFS用的是后续遍历 所以采用的是逆向邻接表
代码
class Solution {
public:
std::unordered_map<int , std::unordered_set<int> > linkedTable;
//返回值 : 是否有环
bool dfs(int node,std::vector<int> & visited, std::vector<int> & res)
{
if(visited[node] ==2 ) return true ;
if( visited[node]==1 ) return false;
visited[node] =2;
for(int x : linkedTable[node] )
{
if(dfs(x,visited, res)) return true;
}
visited[node]=1;
res.push_back(node);
return false;
}
//因为DFS用到stack FILO ,使用邻接表
//DFS
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
for(int i=0;i<prerequisites.size();i++)
{
linkedTable[ prerequisites[i][0] ].emplace(prerequisites[i][1]);
}
std::vector<int> visited(numCourses,0);
std::vector<int> res;
for(int i=0;i<numCourses;i++)
{
if(dfs(i,visited,res)) return {}; //
}
return res;
}
};