• 拓扑排序之课程表问题 bfs, dfs 双解


            拓扑排序指的是有向无环图(DAG),「拓扑排序」是专门应用于有向图的算法。

            下面就是一个拓扑结构;

    拓扑序就是,图中任意一对顶点u和v,若边∈E(G),则u在线性序列中出现在v之前

    我们可以发现拓扑序不是唯一的;

    接下来,我们需要知道一个概念——度:

    对于有向图的某个结点来说,我们把指向它的边的数量叫做入度;

    把从它发出的边的数量称为出度

    拓扑序的问题有两个条件:不存在环,有向

     

    题目:

            这道题用 BFS 和 DFS 都可以完成,BFS 的写法很经典,BFS 的写法就叫「拓扑排序」,这里还用到了贪心算法的思想,贪的点是:当前让入度为 0 的那些结点入队。


    BFS的代码思路:

    BFS表示广度优先遍历,即每次把节点的邻居都访问完了,再继续访问下一层的节点。使用了队列存储节点的顺序。

    1)首先遍历得到每个节点的入度数,以及有向图的字典,即每个节点课程结束后,哪些课程可以继续执行的字典映射。比如,节点1,2,依赖节点0的执行,所以在字典中{ 0: [1,2] }

    2)然后建立一个队列,队列具有先进先出的特点,先把入度为0的点,加进队列中。表示这些节点的课可以先修完,他们不依赖其它课程。

    3)最后,建立队列的循环,每次出一个节点,知道所有入度为0的节点都出来了,队列为空退出循环,此时队列一次出来的节点顺序就是所求答案。

    1. class Solution:
    2. def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]:
    3. if len(prerequisites)==0:
    4. return list(range(numCourses))
    5. in_degree=[0]*numCourses
    6. adj=[set() for _ in range(numCourses)]
    7. for s, f in prerequisites:
    8. in_degree[s]+=1
    9. adj[f].add(s)
    10. queue=[]
    11. res=[]
    12. for i in range(numCourses):
    13. if in_degree[i]==0:
    14. queue.append(i)
    15. while len(queue)>0:
    16. cur=queue.pop(0)
    17. res.append(cur)
    18. for succe in adj[cur]:
    19. in_degree[succe]-=1
    20. if in_degree[succe]==0:
    21. queue.append(succe)
    22. if len(res)!=numCourses:
    23. return []
    24. return res

    DFS的代码思路:

            DFS表示深度优先遍历。指遍历节点的一条路径到底,由图的低部向上回溯,中间的节点通过栈的结构存储,先进后出即为所求答案。

    1)遍历每个节点,同样先建立每个节点的依赖关系字典结构。

    2)建立有n个节点的visited数组,记录每个节点是否在访问,访问中,访问结束。以及是否有环的标记hasCycle。有环的情况下,无法输出拓扑排序。

    3)写dfs函数,传入当前的节点。把该节点的visited标记为1,表示正在访问。

    如果依赖字典中不存在该节点,则可以直接标记visited=2,表示已经访问过,stack 压入该节点。

    否则遍历每一个依赖该节点的邻居节点,

    • 如果visited[node]=0,表示未访问过,递归将该节点进入dfs函数,结束后判断是否hasCycle=True,如果是则遇到环,退出函数。
    • 如果visited[node]=1表示正在访问中又进入了,则存在环hasCycle=True, 退出函数。

    最后,将visited[node]=2,表示进入dfs的这个节点已经访问过了,避免重复访问,并把该节点压入stack栈。

    4) 依次遍历每个节点,如果visited[node]==0则进入dfs函数。最后将stack依次弹出,即为所求答案。栈的弹出对于list来说,就是把每个数反着输出。

    如果hasCycle=True,则有环,没有拓扑顺序。

    1. class Solution:
    2. def findOrder(self, numCourses: int, prerequisites):
    3. def dfs(s):
    4. nonlocal hasCycle
    5. visited[s]=1
    6. if s in graph:
    7. for v in graph[s]:
    8. if visited[v]==0:
    9. dfs(v)
    10. if hasCycle:
    11. return
    12. elif visited[v]==1:
    13. hasCycle=True
    14. return
    15. visited[s]=2
    16. stack.append(s)
    17. return
    18. visited=[0]*numCourses
    19. hasCycle=False
    20. graph={}
    21. stack=[]
    22. for s,f in prerequisites:
    23. if f not in graph:
    24. graph[f]=[s]
    25. else:
    26. graph[f].append(s)
    27. for i in range(numCourses):
    28. if visited[i]==0 and not hasCycle:
    29. dfs(i)
    30. return stack[::-1] if not hasCycle else []

    复习一下C++的BFS实现:

    1. class Solution {
    2. private:
    3. vector <int> in_degree;
    4. vector <int> result;
    5. vector int>> edges;
    6. public:
    7. vector<int> findOrder(int numCourses, vectorint>>& prerequisites) {
    8. edges.resize(numCourses);
    9. in_degree.resize(numCourses);
    10. for (const auto& info: prerequisites){
    11. edges[info[1]].push_back(info[0]);
    12. ++in_degree[info[0]];
    13. }
    14. queue<int> q;
    15. for(int i=0;i
    16. if (in_degree[i]==0){
    17. q.push(i);
    18. }
    19. }
    20. while(!q.empty()){
    21. int u=q.front();
    22. q.pop();
    23. result.push_back(u);
    24. for(int v:edges[u]){
    25. --in_degree[v];
    26. if (in_degree[v]==0){
    27. q.push(v);
    28. }
    29. }
    30. }
    31. if (result.size() !=numCourses){
    32. return {};
    33. }
    34. return result;
    35. }
    36. };

  • 相关阅读:
    RabbitMQ工作模式-主题模式
    Spring Authorization Server入门 (十八) Vue项目使用PKCE模式对接认证服务
    Python virtualenv工具设置虚拟环境和VS code调试Python
    信创操作系统--麒麟Kylin桌面操作系统 (项目十 安全中心)
    Vue中methods实现原理
    开发平台模块化重构
    DeepSpeed教程
    数据结构与算法解题-20240422
    中南林业科技大学数据库实验七:存储过程和触发器
    python基础(四、循环语句)
  • 原文地址:https://blog.csdn.net/u010420283/article/details/126324780