python-leetcode 52.课程表

发布于:2025-03-15 ⋅ 阅读:(10) ⋅ 点赞:(0)

题目:

这个学期必须选修numCourses门课程,记为0到numCourse-1

在选修某些课程之前需要一些先修课程。先修课程按数组prerequisites给出,其中prerequisites[i] = [ai, bi],表示如果要学习课程ai,则必须先学习课程b1。

例如,先修课程对[0,1]表示:想要学习课程0,需要先完成课程1

请判断是否可能完成所有课程的学习?如果可以,返回True,否则,返回False

 


 


方法一:深度优先搜索

以出度为0进行优先搜索 

如果某一门课程的出度为0表示为相对高阶的课程,出度不为0表示它一定是其他某些课程的先修课程,由浅入深,优先寻找高阶的课程,引入三个状态,依次寻找高阶课程。并将连接它的低阶课程出度减去1,为0后即入栈。依次进行。栈的顺序是先入后出,所以高阶课程是后学习的

import collections

class Solution:
    def canFinish(self, numCourses, prerequisites):
        """
        :type numCourses: int
        :type prerequisites: List[List[int]]
        :rtype: bool
        """
        edges=collections.defaultdict(list)#存储课程的依赖关系
        visited=[0]*numCourses #记录访问状态0:未访1:正在访问(递归栈中)2:已访问完成
        result=[]#存储拓扑排序的结果
        self.valid=True#表示是否可以完成所有课程,初始化为 True
        for info in prerequisites:
            edges[info[1]].append(info[0])#将 (b -> a) 关系存入邻接表

        def dfs(u):  #遍历 u 及其所有依赖课程
            if not self.valid: #如果 self.valid 变为 False,则直接返回
                return 
            visited[u]=1 #状态设为1
            for v in edges[u]:
                if visited[v]==0: #如果 v 未访问(visited[v] == 0),递归调用
                    dfs(v)
                    if not self.valid:
                        return
                elif visited[v]==1: #如果 v 正在访问(visited[v] == 1),说明检测到环
                    self.valid=False
                    return
            visited[u]=2
            result.append(u)
        for i in range(numCourses): #遍历 numCourses 中的每个课
            if self.valid and visited[i]==0: #如果 i 未被访问,并且为True
                dfs(i)
        return self.valid

 时间复杂度:O(m+n)n 为课程数,m 为先修课程的要求数。

空间复杂度:O(m+n)需要存储成邻接表的形式,空间复杂度为 O(n+m)。在深度优先搜索的过程中,我们需要最多 O(n) 的栈空间(递归)进行深度优先搜索,因此总空间复杂度为 O(n+m)


方法二:广度优先搜索

 新建一个数组,课程序号作为索引,先修课程的数目入度作为值,入度为0表示先修课程已经完成,或者没有先修课程,现在可以学习这门课程。新建一个队列,将当前可以学习的课程即入度值为0的课程,加入队列中。先学习C1,则以C1为入度的课程值可以减1,C3和C8的入度值减1,此时C8的值为0,将其加入带学习的对列。开始学习C2,C2学习完后,C3,C5减去1,C3,C5的入度为0,加入待学习的对列。重复此过程,将入度为0 的课程加入待学习的队列,直到所有课程学习结束。

import collections

class Solution:
    def canFinish(self, numCourses, prerequisites):
        """
        :type numCourses: int
        :type prerequisites: List[List[int]]
        :rtype: bool
        """
        edges=collections.defaultdict(list)#构建一个默认字典,键是课程编号,值是该课程的后继课程列表,defaultdict,确认访问不存在的键时不会报错,而是自动创建一个空列表
        indeg=[0]*numCourses #存储每门课程的入度,即该课程需要的先修课程的数量,初始化为0
        for info in prerequisites:#遍历课程数组,他是二维的【【a,b】】,学习 a 需要先学习 b
            edges[info[1]].append(info[0]) #b->a
            indeg[info[0]]+=1 #a 的入度 +1

        q=collections.deque([u for u in range(numCourses) if indeg[u]==0])#入度为 0 的节点入队
        visited=0 #记录访问的课程数

        while q:
            visited+=1 # # 每出队一个节点,表示完成了一门课程
            u=q.popleft()  #取出队首节点
            for v in edges[u]:# 遍历所有相邻节点
                indeg[v]-=1 # 依赖于 u 的课程入度 -1
                if indeg[v]==0:#如果某门课程入度变为 0,说明可以学习
                    q.append(v) # 加入队列
        return visited==numCourses ## 如果所有课程都能被访问,说明可以完成所有课程

 时间复杂度:O(m+n)n 为课程数,m 为先修课程的要求数。

空间复杂度:O(m+n)需要存储成邻接表的形式,空间复杂度为 O(n+m)。在深度优先搜索的过程中,我们需要最多 O(n) 队列空间迭代进行广度优先搜索,因此总空间复杂度为 O(n+m)

源自力扣官方题解


网站公告

今日签到

点亮在社区的每一天
去签到