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 。这是不可能的。
 

提示:

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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析:

        好久没写关于图的题了,一看到这道题,脑子里就想到了拓扑排序,但是如何拓扑排序已经忘的一干二净了。。。。而且图的存储结构也忘了,差点想写一个node结构体来存储图。。。

        没办法,只能看题解了。题解使用邻接表来存储图的结构,我感觉这也算是比较直观易懂的一种存储图的方式。并且拓扑排序要求图里不能有环存在。

        每个节点有三种状态,-1:未访问,0:正在访问,1:已访问。0:正在访问,代表以该节点为根,对能到达的相邻节点进行dfs,在尚未遍历完相邻节点时,该根节点标记为0:正在访问。因此,如果访问相邻节点时,它的状态已经是0:正在访问,说明图中有环(a对相邻节点b进行dfs,b对相邻节点a进行dfs),直接返回false;如果相邻节点状态为1:已访问,直接跳过(相邻节点已访问,说明以该相邻节点为根的子图已经全部被遍历完)。当访问完所有节点时,标记该根节点为1:已访问。

        在dfs函数外层,通过for循环来对每个节点进行dfs,跳过其中已访问的节点。如果访问完整个图都没有环存在,则该图可以进行拓扑排序,返回true。

        更详细的分析,请看题解:

https://leetcode-cn.com/problems/course-schedule/solution/ke-cheng-biao-by-leetcode-solution/

代码如下:

class Solution {
public:
    // 邻接表来存储图的结构
    vector<vector<int>> linjietable;
    // visited用来记录某节点是否被访问,共有三个状态
    // -1 未访问
    // 0 正在访问
    // 1 已访问
    int visited[100005];
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        // memset用来初始化visited数组为-1,即所有节点都没有访问过
        memset(visited,-1,sizeof(visited));
        // 重新linjietable邻接表的大小
        linjietable.resize(numCourses);
        // 根据 prerequisites来构建邻接表
        for(int i=0;i<prerequisites.size();i++)
            linjietable[prerequisites[i][1]].push_back(prerequisites[i][0]);
        // 遍历每个节点
        for(int i=0;i<numCourses;i++)
        {
            // 如果该节点没有被访问过,就对该节点进行dfs
            if(visited[numCourses]==1)
            continue;
            bool flag=dfs(prerequisites,i);
            // 如果在dfs的过程中,发现图中有环,则返回false
            if(flag==false)
            return false;

        }
        return true;
        

    }
    // dfs的功能:以index为根节点,来对该节点所在的子图进行dfs,访问该子图中的所有节点
    // 如果子图中有环,则返回false
    bool dfs(vector<vector<int>>& prerequisites,int index)
    {
        // 得到以该节点为根的相邻节点的数量
        int len=linjietable[index].size();
        // 相邻节点数量为0,则直接返回true,因为已经遍历完了,且不存在环
        if(len==0) return true;
        // 当前节点设置为正在访问
        visited[index]=0;
        // 依次对相邻节点进行dfs
        for(int i=0;i<len;i++)
        {
            // 如果该相邻节点已访问,则跳过(相邻节点已访问,说明以该相邻节点为根的子图已经全部被遍历完)
            if(visited[linjietable[index][i]]==1)
            continue;
            // 如果该相邻节点正在访问,说明图中有环 直接返回false
            if(visited[linjietable[index][i]]==0)
            return false;
            // 相邻节点没有被访问,则对相邻节点进行dfs
            bool flag=dfs(prerequisites,linjietable[index][i]);
            // 如果在对相邻节点进行dfs的过程中,发现了环,则直接返回false
            if(flag==false)
            return false;

        }
        // 相邻节点全部访问完毕,则该节点标记为已访问
        visited[index]=1;
        return true;


    }
};

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值