【力扣hot100】207 课程表(c++、python)解析

1.1深搜

从出度思考(从后往前排序), 出度为0的节点在拓扑排序中一定排在后面, 然后删除和该节点对应的边, 迭代寻找出度为0的节点

我们可以将深度优先搜索的流程与拓扑排序的求解联系起来,用一个栈来存储所有已经搜索完成的节点。

对于一个节点 u,如果它的所有相邻节点都已经搜索完成,那么在搜索回溯到u的时候,u本身也会变成一个已经搜索完成的节点。这里的「相邻节点」指的是从u出发通过一条有向边可以到达的所有节点。

假设我们当前搜索到了节点u,如果它的所有相邻节点都已经搜索完成,那么这些节点都已经在栈中了,此时我们就可以把u入栈。可以发现,如果我们从栈顶往栈底的顺序看,由于 u 处于栈顶的位置,那么 u 出现在所有 u 的相邻节点的前面。因此对于 u 这个节点而言,它是满足拓扑排序的要求的。

这样以来,我们对图进行一遍深度优先搜索。当每个节点进行回溯的时候,我们把该节点放入栈中。最终从栈顶到栈底的序列就是一种拓扑排序。
在这里插入图片描述
在这里插入图片描述

C++版本
//一个二维向量,用于表示课程之间的依赖关系,每个元素`edges[u]`表示依赖先修课程`u`的课程。
//`visited`:用于标记课程的访问状态,0表示未访问,1表示正在访问,2表示已访问。
//`valid`:表示当前的课程安排是否有效,初始值为`true`
class Solution{
private:
    vector<vector<int>> edges; 
    vector<int> visited;
    bool valid = true;

public:
//用于从课程`u`开始进行深度优先遍历。在遍历过程中,会对课程进行标记,同时检查是否存在环路。若存在环路,则将`valid`设为`false`,表示无法完成所有课程。
    void dfs(int u) 
    {
        visited[u] = 1;//从课程`u`开始进行深度优先遍历
        for(int v:edges[u])//遍历课程u的先修课程
        {
            if(visited[v] == 0)//如果有没访问的先修课程
            {
                dfs(v);//dfs
                if(!valid)
                {
                    return;
                }
            }
            else if(visited[v] ==1)//出现环路
            {
                valid = false;//直接置为false
                return;
            }
        }
        visited[u] =2;//u这个课程 已访问结束
    }
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites)//函数实现
    {
        //根据输入的先修课程信息,构建课程之间的依赖关系
        edges.resize(numCourses);
        visited.resize(numCourses);
        for (const auto& info:prerequisites)
        {
            edges[info[1]].push\_back(info[0]);
//举个例子,在 prerequisites 列表中有一个元素 [1, 0],意味着课程 1 的先修课程是课程 0。那么在 edges 字典中,edges[0] 
//就是存储了依赖于课程 0 的所有课程的列表。而在这一行代码中,就将课程 1 的编号添加到了 edges[0] 对应的列表中,表示课程 1 依赖于课程 0
        }
        for(int i = 0; i<numCourses &&valid; ++i)
        {
            if(!visited[i])
            {
                dfs(i);
            }
        }
        return valid;
    }
};

Python版本
class Solution:
    #它接受两个参数:`numCourses`表示课程总数,`prerequisites`是一个二维列表,表示课程之间的先修关系。函数返回一个布尔值,表示是否能够完成所有课程
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        edges = collections.defaultdict(list) #导入Python的collections模块,用于创建默认字典使得在后续添加元素时不需要手动创建空列表。
        visited = [0] \* numCourses #创建了一个长度为`numCourses`的列表`visited`用于标记课程的访问状态,初始值都为0。
        result = list()#创建一个空列表`result`,用于存储访问过的课程的结果
        valid = True #表示当前的课程安排是否有效,默认为True。

        #将课程之间的依赖关系添加到`edges`中
        for info in prerequisites:
            edges[info[1]].append(info[0])#表示先修课程info[1] 是 现在课程info[0] 的先修课程,把现在的课程添加到先修课程的edges中
    
        def dfs(u:int):
            #在函数内部使用`nonlocal valid`来声明`valid`变量为非局部变量,以便在内部函数中修改外部函数的变量
            nonlocal valid
            visited[u] = 1#首先标记当前课程为已访问状态(1),然后对当前课程的所有依赖课程进行遍历
            for v in edges[u]:
                if visited[v] == 0:
                    dfs(v)#如果依赖课程未被访问过,则递归调用`dfs`函数
                    if not valid:
                        return
                elif visited[v] == 1:# 如果依赖课程正在被访问中(标记为1),表示存在环路,将`valid`设为False,并直接返回
                    valid = False
                    return
            visited[u] = 2
            result.append(u)#遍历完所有依赖课程后,将当前课程标记为已完成状态(2),并将其添加到结果列表中
        for i in range(numCourses):
            if valid and not visited[i]:
                dfs(i)

        return valid    
      

1.2广搜

先学C1, 为0直接加入队列中
在这里插入图片描述
C3和C8都包含C1的入度,-1,C8加入队列中(学完一门课后更新入度,为0加入队列)
在这里插入图片描述
有向有环图无法执行
在这里插入图片描述
在这里插入图片描述
方法一的深度优先搜索是一种「逆向思维」:最先被放入栈中的节点是在拓扑排序中最后面的节点。我们也可以使用正向思维,顺序地生成拓扑排序,这种方法也更加直观。

我们考虑拓扑排序中最前面的节点,该节点一定不会有任何入边,也就是它没有任何的先修课程要求。当我们将一个节点加入答案中后,我们就可以移除它的所有出边,代表着它的相邻节点少了一门先修课程的要求。

如果某个相邻节点变成了「没有任何入边的节点」,那么就代表着这门课可以开始学习了。按照这样的流程,我们不断地将没有入边的节点加入答案,,直到答案中包含所有的节点(得到了一种拓扑排序)或者不存在没有入边的节点(图中包含环)

上面的想法类似于广度优先搜索,因此我们可以将广度优先搜索的流程与拓扑排序的求解联系起来。

C++版本
//广搜
class Solution{
    private:
        vector<vector<int>> edges; //这个列表存储了依赖于当前课程的先修课程的所有课程的编号
        vector<int> indeg; //一个一维向量,用于表示课程的入度(即指向该课程的边的数量)
    public:
        bool canFinish(int numCourses, vector<vector<int>>& prerequisites)
        {
            edges.resize(numCourses);
            indeg.resize(numCourses);
            for(const auto & info:prerequisites)
            {
                //根据输入的先修课程信息,构建课程之间的依赖关系,并更新课程的入度
                edges[info[1]].push\_back(info[0]); //这个列表存储了依赖于当前课程的先修课程info[1]的所有课程info[0]的编号
                ++indeg[info[0]];//增加现在课程的入度
            }
            queue<int> q;//队列q
            for(int i = 0;i<numCourses;++i)
            {
                if(indeg[i] == 0)//如果入度为0
                {
                    q.push(i);//直接push到队列中
                }
            }
            int visited = 0;
            while (!q.empty())
            {
                ++visited;
                int u = q.front();
                q.pop();
                for(int v:edges[u])
                {
                    --indeg[v];
                    if(indeg[v] == 0)
                    {
                        q.push(v);
                    }
                }
            }
            return visited == numCourses;
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

**深知大多数Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

**因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

![img](https://img-blog.csdnimg.cn/img_convert/58a373c8fb802876b4a1b8a3523ce9e9.png)

 

![img](https://img-blog.csdnimg.cn/img_convert/454e70827b676337a85e32e08be9813d.png)

![img](https://img-blog.csdnimg.cn/img_convert/46506ae54be168b93cf63939786134ca.png)

![img](https://img-blog.csdnimg.cn/img_convert/252731a671c1fb70aad5355a2c5eeff0.png)

![img](https://img-blog.csdnimg.cn/img_convert/6c361282296f86381401c05e862fe4e9.png)

![img](https://img-blog.csdnimg.cn/img_convert/9f49b566129f47b8a67243c1008edf79.png)

 

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!**

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**

**如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注Python)**

67243c1008edf79.png)

 

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!**

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**

**如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注Python)**

<img src="https://img-community.csdnimg.cn/images/fd6ebf0d450a4dbea7428752dc7ffd34.jpg" alt="img" style="zoom:50%;" />
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值