• 207.课程表 | 210.课程表II


    207.课程表

    你这个学期必须选修 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 。这是不可能的。

    思路:环检测,先利用边的信息得到邻接矩阵,再利用path判断是否成环。

    1. class Solution {
    2. public:
    3. vector<bool> visited;
    4. vector<bool> path;
    5. bool res=false;
    6. bool canFinish(int numCourses, vectorint>>& prerequisites) {
    7. visited.resize(numCourses,false);
    8. path.resize(numCourses,false);
    9. vectorint>> graph(numCourses,vector<int>(numCourses,0));
    10. build(prerequisites,graph);//利用边,创建好邻接矩阵
    11. for(int i=0;i//遍历图中的所有节点
    12. {
    13. if(!visited[i])//因为图不一定是联通的,可能存在多个子图
    14. {
    15. traverse(graph,i);
    16. }
    17. }
    18. return !res;
    19. }
    20. void build(vectorint>>& prerequisites,vectorint>>& graph)
    21. {
    22. for(vector<int> edge:prerequisites)
    23. {
    24. int from=edge[1];
    25. int to=edge[0];
    26. graph[from][to]=1;
    27. }
    28. }
    29. void traverse(vectorint>>& graph,int i)
    30. {
    31. if (path[i])
    32. //成环,就把res置为true,直接返回
    33. //这个循环判断要写到if(visited[i]){return}前面,不然人家直接return了,就没你什么事了。
    34. {
    35. res= true;
    36. return ;
    37. }
    38. if (visited[i])
    39. {
    40. return;
    41. }
    42. visited[i]=true;
    43. path[i]=true;
    44. for(int j=0;jsize();j++)
    45. {
    46. if(graph[i][j]==1)
    47. {
    48. traverse(graph,j);
    49. }
    50. }
    51. path[i]=false;
    52. }
    53. };

     210.课程表II

    现在你总共有 numCourses 门课需要选,记为 0 到 numCourses - 1。给你一个数组 prerequisites ,其中 prerequisites[i] = [ai, bi] ,表示在选修课程 ai 前 必须 先选修 bi 。

    例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示:[0,1] 。
    返回你为了学完所有课程所安排的学习顺序。可能会有多个正确的顺序,你只要返回 任意一种 就可以了。如果不可能完成所有课程,返回 一个空数组 。

    示例 1:

    输入:numCourses = 2, prerequisites = [[1,0]]
    输出:[0,1]
    解释:总共有 2 门课程。要学习课程 1,你需要先完成课程 0。因此,正确的课程顺序为 [0,1] 。
    示例 2:

    输入:numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]
    输出:[0,2,1,3]
    解释:总共有 4 门课程。要学习课程 3,你应该先完成课程 1 和课程 2。并且课程 1 和课程 2 都应该排在课程 0 之后。
    因此,一个正确的课程顺序是 [0,1,2,3] 。另一个正确的排序是 [0,2,1,3] 。

    思路一:拓扑排序(后序遍历逆序就是拓扑排序的结果)

    1. class Solution {
    2. public:
    3. vector<int> path;
    4. vector<int> visited;
    5. bool res=false;
    6. vector<int> postorder;
    7. vector<int> findOrder(int numCourses, vectorint>>& prerequisites)
    8. {
    9. vectorint>> graph;
    10. graph.resize(numCourses);
    11. path.resize(numCourses);
    12. visited.resize(numCourses);
    13. build(prerequisites,graph);//根据边,建立邻接表
    14. for(int i=0;i//遍历每一个结点
    15. {
    16. if(!visited[i]) {
    17. traverse(graph,i);
    18. }
    19. }
    20. if(res)//如果成环,则不存在拓扑排序,返回空数组
    21. {
    22. return vector<int>();
    23. }
    24. reverse(postorder.begin(),postorder.end());//后序遍历结果逆序
    25. return postorder;
    26. }
    27. void traverse(vectorint>>& graph,int i)
    28. {
    29. if(path[i])
    30. {
    31. res=true;
    32. return;
    33. }
    34. if(visited[i])
    35. {
    36. return ;
    37. }
    38. visited[i]=true;
    39. path[i]=true;
    40. for(int j:graph[i])
    41. {
    42. traverse(graph,j);
    43. }
    44. postorder.push_back(i);//把后序遍历的结点添加到数组中
    45. path[i]=false;
    46. }
    47. void build(vectorint>>& prerequisites,vectorint>>& graph)
    48. {
    49. for(vector<int> edge:prerequisites)
    50. {
    51. int from=edge[1];
    52. int to=edge[0];
    53. graph[from].push_back(to);
    54. }
    55. }
    56. };

    思路二:拓扑排序(广度优先遍历BFS)

    1、构建邻接表。

    2、构建一个 indegree 数组记录每个节点的入度。

    3、对 BFS 队列进行初始化,将入度为 0 的节点首先装入队列。

    4、开始执行 BFS 循环,不断弹出队列中的节点(弹出结点的顺序就是拓扑排序的顺序),减少相邻节点的入度,并将入度变为 0 的节点加入队列

    5、如果最终所有节点都被遍历过(count 等于节点数),则说明不存在环,反之则说明存在环

    1. class Solution {
    2. public:
    3. queue<int> q;
    4. bool flag=false;
    5. vector<int> res;
    6. vector<int> findOrder(int numCourses, vectorint>>& prerequisites)
    7. {
    8. vectorint>> graph;
    9. graph.resize(numCourses);
    10. build(prerequisites,graph);//建立邻接表
    11. vector<int> indegree;
    12. indegree.resize(numCourses);
    13. for(vector<int> edge:prerequisites)//建立indegree数组记录每个结点的入度
    14. {
    15. indegree[edge[0]]++;
    16. }
    17. bfs(graph,indegree);//广度优先遍历
    18. if(flag)//不成环,拓扑排序成功
    19. {
    20. return res;
    21. }
    22. else//成环,返回空容器,无法拓扑排序
    23. return vector<int>();
    24. }
    25. void bfs(vectorint>>& graph,vector<int>& indegree)
    26. {
    27. for(int i=0;isize();i++)//先把入度为零的结点入队列
    28. {
    29. if(indegree[i]==0)
    30. {
    31. q.push(i);
    32. }
    33. }
    34. int count=0;
    35. while(!q.empty())
    36. {
    37. count++;
    38. int cur=q.front();//弹出队头结点
    39. res.push_back(cur);
    40. q.pop();
    41. for(int j:graph[cur])
    42. {
    43. indegree[j]--;//相邻结点入度减一
    44. if(indegree[j]==0)//如果入度减到0,加入队列中
    45. {
    46. q.push(j);
    47. }
    48. }
    49. }
    50. if(count==indegree.size())//所有结点都入过队列,无环,拓扑排序成功
    51. {
    52. flag=true;
    53. }
    54. }
    55. void build(vectorint>>& prerequisites,vectorint>>& graph)
    56. {
    57. for(vector<int> edge:prerequisites)
    58. {
    59. int from=edge[1];
    60. int to=edge[0];
    61. graph[from].push_back(to);
    62. }
    63. }
    64. };

  • 相关阅读:
    每日学习总结20240313
    【Ping检测】使用Ping命令检查网络连接情况
    jenkins 部署 和构建java项目
    信息检索 | 信息检索方法一览
    C++ 矩阵的最小路径和解法
    代码随想录算法训练营day53||1035.不相交的线||53. 最大子序和
    科普:什么是视频监控平台?如何应用在场景中?
    基于CGAN-LSTM的无监督网络异常流量检测算法
    【CKA考试笔记】十四、helm
    负载均衡器监控
  • 原文地址:https://blog.csdn.net/weixin_50437588/article/details/126282410