[算法题]课程表/课程表 II

题目链接: 

课程表

课程表 II

通过拓扑排序求解, 首先认识有向无环图:

入度表示有多少点指向自己, 出度表示自己指向多少点, 拓扑排序的思想则为选出入度为 0 的点排, 然后将被选出的点指向的点的入度减 1, 当入度被减到 0 时表示该点可以被选出, 一直循环直到全部点被选出或不存在入度为 0 的点.

拓扑排序大致步骤:

1. 建图

可以通过 STL 容器简单建图, 如: vector<vector<int>>, unordered_map<int, vector<int>>, 均表示一个点指向了其他几个点

2. 通过一个一维数组存储各个点的入度, 下标表示该点, 值表示入度

3. 定义一个队列, 将起始入度为 0 的点入队

4. 做一次 bfs, 因为队中的元素都是入度为 0 的点, 所以每次取出对头的点进行拓扑排序, 并同时将被该点指向的点的入度减减, 为 0 时入队, 以此往复直到队列为空

5. 最后判断保存入度的一维数组是否不存在入度大于 0 的点, 如果不存在表示拓扑排序成功, 反之表示存在环

题目中指出: [ai, bi] ,表示在选修课程 ai 前 必须 先选修 bi

表示 bi->ai,根据此规则建图,在建图的过程中同时保存各点的入度,再将入度为 0 的点入队,最后进行一次 bfs 即可,注意两道题非常相像,无非是对入度为 0 的点的处理方式不同,"课程表"这题无需对点进行特殊处理,只需在最后判断是否有环即可,"课程表 II"需要将点添加到一个 vector中,返回拓扑排序后的结果.

"课程表"题解代码:

class Solution 
{
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) 
    {
        unordered_map<int, vector<int>> edges; //建图
        vector<int> in(numCourses); //保存入度
        for(const auto& vv : prerequisites)
        {
            edges[vv[1]].push_back(vv[0]); //注意指向
            in[vv[0]]++; //更新入度
        }
        queue<int> q;
        //将当前入度为0的加入到队列
        for(int i = 0; i < numCourses; ++i)
        {
            if(!in[i])
            {
                q.push(i);
            }
        }
        //bfs
        while(!q.empty())
        {
            int n = q.front();
            q.pop();
            //更新入度,当经过减减后入度为0则添加到队列
            for(const auto& e : edges[n])
            {
                if(--in[e] == 0)
                {
                    q.push(e);
                }
            }
        }
        //判断是否存在环,有环返回false
        for(const auto& e : in)
        {
            if(e)
            {
                return false;
            }
        }
        return true;
    }
};

"课程表 II"题解代码:

class Solution 
{
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) 
    {
        vector<vector<int>> arr(numCourses); //建图
        vector<int> in(numCourses); //保存入度
        for(const auto& vv : prerequisites)
        {
            arr[vv[1]].push_back(vv[0]); //注意指向
            in[vv[0]]++; //更新入度
        }
        queue<int> q;
        //将当前入度为0的加入到队列
        for(int i = 0; i < numCourses; ++i)
        {
            if(!in[i])
            {
                q.push(i);
            }
        }
        vector<int> res; //保存拓扑排序后的结果
        //bfs
        while(!q.empty())
        {
            int n = q.front();
            q.pop();
            res.push_back(n);
            for(const auto& e : arr[n])
            {
                if(--in[e] == 0)
                {
                    q.push(e);
                }
            }
        }
        //判断是否存在环
        for(const auto& e : in)
        {
            if(e)
            {
                return {};
            }
        }
        return res;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值