207. 课程表

import collections
from typing import List, Dict, Deque

class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        """
        判断是否能完成所有课程的学习(拓扑排序 - Kahn 算法)。
        假设 prerequisites 中的 [A, B] 表示 A 是 B 的先修课程 (A -> B)。

        Args:
            numCourses: 课程总数。
            prerequisites: 先修课程要求列表,[[A, B], ...] 表示 A -> B。

        Returns:
            如果可以完成所有课程,返回 True;否则返回 False。
        """
        if numCourses <= 0:
            return True
        
        # 1. 初始化入度数组和邻接表
        # ingree[i] 存储课程 i 的入度
        ingree = [0] * numCourses 
        # adj[i] 存储以课程 i 为先修课的所有后续课程列表
        adj: Dict[int, List[int]] = collections.defaultdict(list)

        # 遍历先修课程列表,构建邻接表并计算入度
        # 这里根据 C++ 代码的逻辑,将 prerequisites[i][0] 视为先修课 A
        # 将 prerequisites[i][1] 视为后续课程 B (A -> B)
        for prerequisite in prerequisites:
            if len(prerequisite) == 2:
                pre = prerequisite[0] # 先修课 A
                cur = prerequisite[1] # 当前课 B
                adj[pre].append(cur)  # 添加 A -> B 的边
                ingree[cur] += 1      # B 的入度加 1
            else:
                # 可以选择忽略无效输入或抛出错误
                pass 

        # 2. 初始化队列,加入所有入度为 0 的课程
        queue: Deque[int] = collections.deque()
        count = 0 # 记录可以完成的课程数量
        for i in range(numCourses):
            if ingree[i] == 0:
                queue.append(i)
                count += 1

        # 3. BFS 过程
        while queue:
            # 取出队首课程(表示完成这门课)
            cur_course = queue.popleft()
            
            # 遍历所有以 cur_course 为先修课的后续课程
            if cur_course in adj:
                for neighbor_course in adj[cur_course]:
                    # 将后续课程的入度减 1
                    ingree[neighbor_course] -= 1
                    # 如果后续课程入度变为 0,则加入队列,并增加计数
                    if ingree[neighbor_course] == 0:
                        queue.append(neighbor_course)
                        count += 1

        # 4. 结果判断
        # 如果完成的课程数量等于总课程数,说明没有环,可以完成
        return count == numCourses

# 示例用法
# solution = Solution()
# numCourses1 = 2
# prerequisites1 = [[1,0]] # 0 -> 1 ? 还是 1 -> 0? 
# # 按照代码逻辑 A->B, 这里是 1 -> 0, 0 依赖 1. 
# # 初始入度: 0:1, 1:0. 队列: [1]. count=1.
# # Dequeue 1. count=1. 邻居 0. ingree[0] 减为 0. Enqueue 0. count=2. 队列 [0].
# # Dequeue 0. count=2. 无邻居. 队列 [].
# # 返回 count==numCourses (2==2) -> True.
# print(solution.canFinish(numCourses1, prerequisites1)) # 输出: True 

# numCourses2 = 2
# prerequisites2 = [[1,0],[0,1]] # 1->0, 0->1 (环)
# # 初始入度: 0:1, 1:1. 队列: []. count=0.
# # 循环不执行.
# # 返回 count==numCourses (0==2) -> False.
# print(solution.canFinish(numCourses2, prerequisites2)) # 输出: False

# numCourses3 = 5
# prerequisites3 = [[0,1],[0,2],[1,3],[1,4],[3,4]] # 0->1, 0->2, 1->3, 1->4, 3->4
# # 初始入度: 0:0, 1:1, 2:1, 3:1, 4:2
# # 队列: [0]. count=1.
# # Dequeue 0. count=1. 邻居 1, 2. ingree[1]=0, ingree[2]=0. Enqueue 1, 2. count=3. 队列 [1, 2].
# # Dequeue 1. count=3. 邻居 3, 4. ingree[3]=0, ingree[4]=1. Enqueue 3. count=4. 队列 [2, 3].
# # Dequeue 2. count=4. 无邻居. 队列 [3].
# # Dequeue 3. count=4. 邻居 4. ingree[4]=0. Enqueue 4. count=5. 队列 [4].
# # Dequeue 4. count=5. 无邻居. 队列 [].
# # 返回 count==numCourses (5==5) -> True.
# print(solution.canFinish(numCourses3, prerequisites3)) # 输出: True

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值