Intuition
Why it’s classic:
while queue loop followed by –> checking len(queue) then for looping thru only the first few nodes that define a “level”len(queue) (in conjunction with first-in-first-out queue appending) allows us to select all the nodes defining a particular “level”/”genertation” that needs to be progressed forward.Main Explanation
from collections import deque
class Solution:
def orangesRotting(self, grid: list[list[int]]) -> int:
rows, cols = len(grid), len(grid[0])
queue = deque()
fresh_count = 0
# preprocess: find all wrotten and fresh oranges
for r in range(rows):
for c in range(cols):
if grid[r][c] == 2:
queue.append((r, c))
elif grid[r][c] == 1:
fresh_count += 1
# base case: 0 fresh oranges => 0 minutes needed to have no fresh fruit
if fresh_count == 0:
return 0
minutes = 0
# Multi-Sourced BFS: Starting with multiple nodes in the queue
# then progressing all of them at once
while queue and fresh_count > 0:
minutes += 1
#process all oranges currently in the queue at the current "level"
for i in range(len(queue)):
r, c = queue.popleft()
for dr, dc in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
nr, nc = r + dr, c + dc
# if neighbor is within bounds and is a fresh orange
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == 1:
# rot it! to track seen fresh oranges
grid[nr][nc] = 2
fresh_count -= 1
queue.append((nr, nc)) # then propogate
# check if fresh oranges remain
return minutes if fresh_count == 0 else -1
Intuition
Why it’s classic
Main Explanation
We need a graph conversion then → graph traversal and checking algorithm → the only way you can’t take all courses is if you have a cycle
Return is courses taken == numCourses
from collections import deque
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
neighbors = [[] for i in range(numCourses)]
indegree = [0] * numCourses
for a, b in prerequisites:
# grouping is prereq --> class that has that prereq
neighbors[a].append(b)
indegree[b] += 1
# add the index of all nodes
# deque here operates conceptually the same as a queue, popleft() operates in O(1)
# if all nodes cycle => all have indegree[i] > 0 => they never enter queue => queue clears early
queue = deque([i for i in range(len(indegree)) if indegree[i] == 0])
taken = 0
while queue:
curr = queue.popleft()
taken += 1
for n in neighbors[curr]:
indegree[n] -= 1
# if we cycle then the nodes never enter the queue => queue clears early
if indegree[n] == 0:
queue.append(n)
return taken == numCourses
from collections import defaultdict
def canFinish(numCourses, prerequisites):
graph = defaultdict(list)
for a, b in prerequisites:
graph[b].append(a)
visited = [0] * numCourses # 0 = unvisited, 1 = visiting, 2 = visited
def has_cycle(course):
if visited[course] == 1: # cycle detected
return True
if visited[course] == 2:
return False
visited[course] = 1
for neighbor in graph[course]:
if has_cycle(neighbor):
return True
visited[course] = 2 # mark branches of node: ‘course’ has possessing no cycle
return False
for i in range(numCourses):
if has_cycle(i):
return False
return True