Leetcode举一反三第二期---课程规划和 BFS的熟悉掌握
文章目录
Leetcode 207 Course Schedule 是拓扑排序的入门题,用 Kahn 算法(BFS + 入度数组)。相信大家都刷过。
今天这篇用一个模板带 4 道题:207、210、1136、2050。
from collections import deque
class Solution:
def canFinish(self, numCourses: int, prerequisites: list[list[int]]) -> bool:
# 1. 建图:入度数组 + 邻接表
indegree = [0] * numCourses
adj = [[] for _ in range(numCourses)]
for course, pre in prerequisites:
adj[pre].append(course) # pre -> course
indegree[course] += 1
# 2. 入度为 0 的课程先入队
queue = deque([i for i in range(numCourses) if indegree[i] == 0])
visited_count = 0
# 3. BFS
while queue:
curr = queue.popleft()
visited_count += 1
for next_course in adj[curr]:
indegree[next_course] -= 1
if indegree[next_course] == 0:
queue.append(next_course)
# 4. 能出队的课程数等于总数,说明没有环
return visited_count == numCourses
好了,下面刷 210 Course Schedule II,要求输出一种选课顺序。 怎么有效的刷变种?怎么压缩信息把他存在你的记忆空间里?答案是,基于基础题,维持原模板,只记需要修改的部分。建图、入度、入队、BFS 全都不动。 见如下: (关注 【修改 】的部分!!!)
from collections import deque
class Solution:
def findOrder(self, numCourses: int, prerequisites: list[list[int]]) -> list[int]:
indegree = [0] * numCourses
adj = [[] for _ in range(numCourses)]
for course, pre in prerequisites:
adj[pre].append(course)
indegree[course] += 1
queue = deque([i for i in range(numCourses) if indegree[i] == 0])
# 【修改 1】:计数器 visited_count 换成结果列表 res
res = []
while queue:
curr = queue.popleft()
# 【修改 2】:出队时把课程追加进 res,出队顺序本身就是拓扑序
res.append(curr)
for next_course in adj[curr]:
indegree[next_course] -= 1
if indegree[next_course] == 0:
queue.append(next_course)
# 【修改 3】:返回值由 bool 改成列表,有环时 res 不满,返回 []
return res if len(res) == numCourses else []
1136 Parallel Courses 问最少要几个学期修完,每学期可以同时修任意多门。 核心是把 BFS 的每一层看成一个学期,改动有 4 处:
𝗟𝗲𝗲𝘁𝗰𝗼𝗱𝗲 𝟭𝟭𝟯𝟲 𝗣𝗮𝗿𝗮𝗹𝗹𝗲𝗹 𝗖𝗼𝘂𝗿𝘀𝗲𝘀
from collections import deque
class Solution:
def minimumSemesters(self, n: int, relations: list[list[int]]) -> int:
# 【修改 1】:课程编号从 1 开始,数组开 n + 1
# 注意 relations 是 [prev, next],跟 207 里的 [course, pre] 顺序相反
indegree = [0] * (n + 1)
adj = [[] for _ in range(n + 1)]
for pre, course in relations:
adj[pre].append(course)
indegree[course] += 1
queue = deque([i for i in range(1, n + 1) if indegree[i] == 0])
visited_count = 0
semesters = 0 # 【修改 2】:新增学期计数
while queue:
semesters += 1 # 【修改 3】:每处理完一整层,就是一个学期
for _ in range(len(queue)): # 【修改 3】:按层遍历,固定本层大小
curr = queue.popleft()
visited_count += 1
for next_course in adj[curr]:
indegree[next_course] -= 1
if indegree[next_course] == 0:
queue.append(next_course)
# 【修改 4】:没修完说明有环返回 -1,否则返回层数
return semesters if visited_count == n else -1
2050 Parallel Courses III 更进一步,每门课有自己的耗时,问最少几个月修完。 现在一层不再等于一个学期,需要记录每门课的最早完成时间,也就是在拓扑序上做 DP: 𝗟𝗲𝗲𝘁𝗰𝗼𝗱𝗲 𝟮𝟬𝟱𝟬 𝗣𝗮𝗿𝗮𝗹𝗹𝗲𝗹 𝗖𝗼𝘂𝗿𝘀𝗲𝘀 𝗜𝗜𝗜
from collections import deque
class Solution:
def minimumTime(self, n: int, relations: list[list[int]], time: list[int]) -> int:
indegree = [0] * (n + 1)
adj = [[] for _ in range(n + 1)]
for pre, course in relations:
adj[pre].append(course)
indegree[course] += 1
# 【修改 1】:finish[i] 表示课程 i 最早能在第几个月修完
finish = [0] * (n + 1)
queue = deque()
for i in range(1, n + 1):
if indegree[i] == 0:
queue.append(i)
finish[i] = time[i - 1] # 【修改 2】:起点课程的完成时间就是自身耗时
while queue:
curr = queue.popleft()
for next_course in adj[curr]:
# 【修改 3】:等最晚的那门前置课修完,再加自己的耗时
finish[next_course] = max(finish[next_course], finish[curr] + time[next_course - 1])
indegree[next_course] -= 1
if indegree[next_course] == 0:
queue.append(next_course)
# 【修改 4】:题目保证无环,不用计数,所有课程里最晚完成的时间就是答案
return max(finish)
这样,一个模板就可以带起 4 道题。 每道题脑子里只多记 3 到 4 个改动点,不用重新背一份代码。可以记住更多的题目了。 按这个思路去试试吧 朋友们。
全文完



