【面试经典 150 | 图】【每日一题】课程表

Tag

【拓扑排序】【2023-09-09】


题目来源

207. 课程表


题目解读

在选修某些课程之前需要先学习某些课程,先学习的课程有数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] 表示为学习课程 ai 之前要先学习课程 bi。请你判断是否可以完成 numCourses 课程数的课程。


解题思路

方法一:拓扑排序

这是一道典型的拓扑排序问题。在学习某些课程之前需要先选修学习一些其他的课程,有先后顺序的限制,因此可以使用拓扑排序来解题。拓扑排序方法的基本步骤如下:

  • 计算每个节点的入度;
  • 将入度为 0 的节点加入到队列 que 中;
  • 依次枚举 que 中的节点 node,将与 node 相连的节点 node2 的入度 -1,如果节点 node2 的入度为 0,则将其加入到队列 que 中;

在枚举队列中的节点时,对节点进行标记或者一些特殊处理从而解决相应的问题。在本题中,在枚举队列中的节点时,维护一个变量 cnts 对节点进行计数,如果枚举结束后 cnts != numCourses 则表明选修限制中存在环,即互为先决条件的情况。

在具体实现中,需要定义一个二维数组来建立课程之间的边,还需要一个一维数组 in 来记录各个课程的入度。首先,遍历课程关系数组 prerequisties 更新课程的度以及课程之间的边关系;接着,将度为 0 的课程编号存入队列 q 中;然后,依次枚举队列中的课程,并更新与当前枚举的课程连接的新的课程的入度 -1,如果更新后新的课程入度为 0,则将入队列中,并且在枚举队列中课程时,将计数变量 visited +1;最后,如果计算变量与课程数量有如下关系:visted = numCourses,则表明可以完成所有课程的学习,否则不能。

实现代码

class Solution {
    vector<vector<int>> edges;      // 课程之间的边
    vector<int> in;                 // 课程的入度
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        edges.resize(numCourses);
        in.resize(numCourses);
        for(auto prerequisite : prerequisites){
            edges[prerequisite[1]].push_back(prerequisite[0]);
            ++ in[prerequisite[0]];
        }

        queue<int> q;
        for(int i = 0; i < in.size(); ++i){
            if(in[i] == 0){
                q.push(i);
            }
        }

        int visited = 0;
        while(!q.empty()){
            int u =q.front();
            q.pop();
            ++ visited;

            for(int i = 0; i < edges[u].size(); ++i){
                -- in[edges[u][i]];
                if(in[edges[u][i]] == 0){
                    q.push(edges[u][i]);
                }
            }
        }
        
        return visited == numCourses;
    }
};

复杂度分析

时间复杂度: O ( n + m ) O(n+m) O(n+m),其中 n n n 为课程数, m m m 选修课程限制数组的长度。

空间复杂度: O ( n + m ) O(n+m) O(n+m),课程之间建立邻接表的空间复杂度为 O ( n + m ) O(n+m) O(n+m)。广搜中使用最多 O ( n ) O(n) O(n) 的队列空间。因此总空间复杂度为 O ( n + m ) O(n+m) O(n+m)

知识回顾

拓扑排序的含义与应用可参考 广度优先搜索(5)之拓扑排序

写在最后

如果文章内容有任何错误或者您对文章有任何疑问,欢迎私信博主或者在评论区指出 💬💬💬。

如果大家有更优的时间、空间复杂度方法,欢迎评论区交流。

最后,感谢您的阅读,如果感到有所收获的话可以给博主点一个 👍 哦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wang_nn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值