Unit 11 Session 2 Advanced (Click for link to problem statements)
Understand what the interviewer is asking for by using test cases and questions about the problem.
- Established a set (2-3) of test cases to verify their own solution later.
- Established a set (1-2) of edge cases to verify their solution handles complexities.
- Have fully understood the problem and have no clarifying questions.
- Have you verified any Time/Space Constraints for this problem?
HAPPY CASE
Input: city = [
[1,2], # Location 0
[2,3], # Location 1
[5], # Location 2
[0], # Location 3
[5], # Location 4
[], # Location 5
[] # Location 6
]
Output: [2,4,5,6]
Explanation: Locations 5 and 6 are terminal. Locations 2 and 4 lead to these terminal nodes.
EDGE CASE
Input: city = [
[1,2,3,4],
[1,2],
[3,4],
[0,4],
[]
]
Output: [4]
Explanation: Only location 4 is a terminal node.
Match what this problem looks like to known categories of problems, e.g. Linked List or Dynamic Programming, and strategies or patterns in those categories.
For Topological Sorting problems, we want to consider the following approaches:
Plan the solution with appropriate visualizations and pseudocode.
General Idea: Perform a reverse topological sort by processing terminal nodes first and marking them as safe. Then, propagate the safety status backward to any nodes that point to terminal nodes, and continue until all safe nodes are found.
1) Initialize an in-degree array to track the number of outgoing edges from each node.
2) Reverse the graph so that edges point to nodes that depend on the current node.
3) Use a queue to process all terminal nodes (nodes with no outgoing edges).
4) For each terminal node, mark it as safe and reduce the in-degree of its predecessors.
5) Continue processing nodes with in-degree 0 until no more nodes can be processed.
6) Return the sorted list of all safe nodes.
⚠️ Common Mistakes
Implement the code to solve the algorithm.
from collections import deque
# Main function to find all eventual safe locations
def eventual_safe_locations(city):
n = len(city)
# Reverse graph and calculate in-degrees
reverse_graph = [[] for _ in range(n)]
in_degree = [0] * n
for node in range(n):
for neighbor in city[node]:
reverse_graph[neighbor].append(node)
in_degree[node] = len(city[node])
# Queue for processing safe nodes (start with terminal nodes)
queue = deque()
for node in range(n):
if in_degree[node] == 0: # Terminal nodes (no outgoing edges)
queue.append(node)
# List to store eventual safe locations
safe_locations = []
while queue:
safe_node = queue.popleft()
safe_locations.append(safe_node)
# For each node that points to the current safe node, reduce its in-degree
for predecessor in reverse_graph[safe_node]:
in_degree[predecessor] -= 1
if in_degree[predecessor] == 0: # If it becomes terminal, add to queue
queue.append(predecessor)
# Return the safe locations in sorted order
return sorted(safe_locations)
Review the code by running specific example(s) and recording values (watchlist) of your code's variables along the way.
Example 1:
Evaluate the performance of your algorithm and state any strong/weak or future potential work.
Assume V
is the number of nodes (locations) and E
is the number of edges (communication lines).
O(V + E)
because we process each node and edge once.O(V + E)
due to the reverse graph and in-degree array.