TIP103 - Unit 7 Session 1 - Graph Algorithm
TIP103 - Unit 7 Session 1 - Graph Algorithm
1
Welcome to Unit 7, Session 1:
Graph Algorithms
2. Rename yourself with your pod number, full name, and pronouns:
2
Graphs Algorithms
TIP103 Unit 7.1
As a community, we will...
4
Agenda
1 Stand Up 0:00 - 0:05
2 Review of BFS/DFS 0:05 - 0:10
3 Topological Sort 0:10 - 0:20
4 UMPIRE Walkthrough 0:20 - 0:35
5 Break 0:35 - 0:40
6 Breakout 0:40 - 1:40
7 Post-Breakout Walkthrough 1:40 - 1:55
8 Wrap Up 1:55 - 2:00
5
Your Feedback
Glows Grows
● Breakout sessions ● Edge lists/sets
● Reviewing/learning ● No TF/coach
graphs ● Java/Python consistency
● Quiet pods
6
5 minutes
Review of BFS/DFS
Review of DFS and BFS
✱ Both are graph traversal algorithms.
✱ They differ in how they determine which node to visit next.
8
DFS
✱ Paths are explored all the way to their leaves before other paths
are considered.
✱ DFS is typically implemented recursively.
✱ There is no guarantee that the first path found is the shortest.
9
Check for Understanding 45 sec
2 3
10
Check for Understanding
11
BFS
✱ Paths are explored by level.
✱ BFS is implemented iteratively, maintaining a FIFO queue of nodes
to visit.
✱ The first path found is guaranteed to be the shortest.
12
Check for Understanding 45 sec
2 3
13
Check for Understanding
14
Check for Understanding 45 sec
F. Node 5
2 3
G. None of the above
15
Check for Understanding
F. Node 5
2 3
G. None of the above
16
Check for Understanding 45 sec
2 3
17
Check for Understanding
2 3
18
10 minutes
Topological Sort
Chop
Veggies
Toast Prepare
Bread Eggs
Sauté
Veggies
Butter
Bread
Add Eggs
and Cook
Which tasks
could we
complete first? Plate Food
20
Topological Sorting
✱ An ordering of nodes in a directed acyclic graph (DAG) such that, for each
directed edge from node A to node B, node A appears before node B in the
ordering.
21
Topological Sorting
1 4
2 3 5 0 1 4 2 3
22
Topological Sorting (DFS)
1. Pick an unvisited source.
2. Do a depth-first traversal starting with that node and only explore
unvisited nodes.
3. After visiting child nodes (post-order), add the parent node to the
front of a list.
4. Repeat until all nodes are visited.
The list will contain a topological sort of the nodes.
23
Topological Sort
1 4
2 3 5 0 1 4 2 3
24
Benefits of Topological Sort
✱ Can be used to
● find the shortest path in a weighted DAG
● determine whether a graph has a cycle
● resolve dependencies (such as determining the order to
perform activities)
✱ Time complexity is O(E + V), where:
● E is the number of edges
● V is the number of vertices
✱ Space complexity is O(V)
● visited set
● stack
25
15 minutes
UMPIRE Walkthrough
Course Schedule https://leetcode.com/problems/course-schedule/
27
U-nderstand
✱ Is prerequisites an edge set?
● Yes, except each pair is in the opposite of the usual order.
● [ai, bi] indicates an edge from bi to ai.
✱ Is prerequisites a DAG?
● Not necessarily! You have to detect if there is a cycle.
✱ How can we tell if there's a cycle?
● Let's draw some pictures…
28
U-nderstand
1 1 1 1
0 0 0 0
2 2 2 2
30
U-nderstand
How can we detect a cycle?
✱ No sources
✱ Not all nodes reachable from sources
1 1
0 0
2 2
31
U-nderstand
How can we detect a cycle?
✱ No sources
✱ Not all nodes reachable from sources
✱ Ancestor and descendant point (possibly indirectly) to each
other.
1 1
0 0
2 2
32
M-atch
✱ Topological sort
✱ Preprocess data
● Convert (backwards) edge set to adjacency list.
● Identify sources.
33
P-lan
1. Preprocess data to build adjacency list and identify sources.
2. Created visited set.
3. Perform DFS from each source:
a. Mark node as visited.
b. Recursively call DFS on all non-visited neighbors.
c. Return false if we identify a cycle. 1
4. If some nodes are unvisited, there must be a cycle.
0
34
P-lan
dfs(i): 1
1. Preprocess data.
2. For every source, call dfs(source). canFinish()
3. Return true if all nodes visited. dfs(0)
35
P-lan
dfs(i): 1
1. Preprocess data.
2. For every source, call dfs(source). canFinish()
3. Return true if all nodes visited. dfs(0) → dfs(2)
36
P-lan
dfs(i): 1
1. Preprocess data.
2. For every source, call dfs(source). canFinish()
3. Return true if all nodes visited. dfs(0) → dfs(2)
dfs(1)
37
P-lan
dfs(i): 1
1. Preprocess data.
2. For every source, call dfs(source). canFinish()
3. Return true if all nodes visited. dfs(0) → dfs(2)
dfs(1)
38
P-lan
dfs(i): 3
canFinish()
dfs(3)
39
P-lan
dfs(i): 3
canFinish()
dfs(3) → dfs(1)
40
P-lan
dfs(i): 3
canFinish()
dfs(3) → dfs(1) → dfs(2)
41
P-lan
dfs(i): 3
canFinish()
dfs(3) → dfs(1) → dfs(2) → dfs(0)
There's a cycle if a neighbor is in the dfs call stack. 42
P-lan
dfs(i): 1
1. Preprocess data.
2. For every source, call dfs(source). canFinish()
3. Return true if all nodes visited. dfs(0) → dfs(2)
dfs(1)
44
I-mplement/R-eview
Python
class Solution:
def canFinish(self, numCourses: int,
prerequisites: List[List[int]]) -> bool:
# Preprocess data to build adjacency list.
visited = set()
for prereq in prerequisites:
adj = [[] for _ in range(numCourses)]
pre, post = prereq
is_source = [True] * numCourses
adj[pre].append(post)
is_source[post] = False
def dfs(i: int, stack: Set[int] = set()):
visited.add(i)
# Perform dfs from source nodes.
stack.add(i)
for i in range(numCourses):
for neighbor in adj[i]:
if is_source[i]:
if neighbor in visited:
if not dfs(i):
# Are we in a cycle?
return False
if neighbor in stack:
return False
# Make sure all nodes have been visited.
else:
return len(visited) == numCourses
if not dfs(neighbor, stack):
return False
stack.remove(i)
return True
45
Java
public boolean canFinish(int numCourses, int[][] prerequisites) {
Set<Integer> visited = new HashSet<>();
Map<Integer, List<Integer>> adjacencyList = new HashMap<>();
boolean[] isSource = new boolean[numCourses];
class Solution {
Arrays.fill(isSource, true);
boolean dfs(int i, Map<Integer, List<Integer>> adj,
// Preprocess data to build adjacency list.
Set<Integer> visited, Set<Integer> stack) {
for (int[] prereq : prerequisites) {
visited.add(i);
int pre = prereq[1];
stack.add(i);
int post = prereq[0];
for (int neighbor : adj.getOrDefault(i, Collections.emptyList())) {
adjacencyList.computeIfAbsent(pre, k -> new ArrayList<>()).add(post);
if (visited.contains(neighbor)) {
isSource[post] = false;
// Are we in a cycle?
}
if (stack.contains(neighbor)) {
// Perform dfs from source nodes.
return false;
for (int i = 0; i < numCourses; i++) {
}
if (isSource[i]) {
} else {
boolean okay = dfs(i, adjacencyList, visited, new HashSet<>());
// See if recursive call succeeds or finds a cycle.
if (!okay) {
boolean okay = dfs(neighbor, adj, visited, stack);
return false;
if (!okay) {
}
return false;
}
}
}
}
if (visited.size() < numCourses) {
}
// There must not have been enough sources.
stack.remove(i);
return false;
return true;
}
}
return true;
}
}
46
E-valuate
Let V be the number of courses and E the number of prerequisites.
sources.
color[i] = self.VISITING
for j in graph[i]:
https://leetcode.com/problems/course-schedule/solutions/3331771/python-elegant-short-three-color-dfs/ 49
Lesson
✱ To someone with a
hammer, everything looks
like a nail.
✱ Pause to think about
whether you are doing
more work than
requested.
50
Break Time!
Take 5 mins to step away from the computer.
Feel free to turn off your camera and return
promptly after 5 mins.
60 minutes
Breakout Sessions
Breakout Rooms 60 minutes
● Today's questions:
Questions?
○ Course Schedule II [Medium] Post on Slack
○ Course Schedule IV [Medium] and tag:
○ Minimum Height Trees [Medium]
○ Reconstruct Itinerary [Hard] @tip103-tas
53
Reflection 1 min
54
15 minutes
Post-Breakout Walkthrough
Minimum Height Trees https://leetcode.com/problems/minimum-height-trees
A tree is an undirected graph in which any two vertices are connected by exactly one path. In other words, any connected graph without
simple cycles is a tree.
Given a tree of n nodes labelled from 0 to n - 1, and an array of n - 1 edges where edges[i] = [ai, bi] indicates that there is an undirected
edge between the two nodes ai and bi in the tree, you can choose any node of the tree as the root. When you select a node x as the
root, the result tree has height h. Among all possible rooted trees, those with minimum height (i.e. min(h)) are called minimum height
trees (MHTs).
Return a list of all MHTs' root labels. You can return the answer in any order.
The height of a rooted tree is the number of edges on the longest downward path between the root and a leaf.
Constraints:
56
Example 1
57
Example 2
59
U-nderstand
✱ The roots will be the nodes furthest from the leaves.
✱ Leaves are nodes that have degree 1.
✱ Can we just throw out the leaves and see what nodes remain?
1 2
3 4 5
6 7 8
60
U-nderstand
✱ The roots will be the nodes furthest from the leaves.
✱ Leaves are nodes that have degree 1.
✱ Can we just throw out the leaves and see what nodes remain?
1 2
3 4 5
6 7 8
61
U-nderstand
✱ The roots will be the nodes furthest from the leaves.
✱ Leaves are nodes that have degree 1.
✱ Can we just throw out the leaves and see what nodes remain?
3 5
62
U-nderstand
✱ The roots will be the nodes furthest from the leaves.
✱ Leaves are nodes that have degree 1.
✱ Can we just throw out the leaves and see what nodes remain?
✱ Yes, but it may take several rounds.
63
M-atch
✱ BFS
64
P-lan
1. Preprocess data
a. Build an adjacency list representation.
b. Calculate the degree of each node.
2. Initialize a queue with nodes with degree 1.
3. While nodes are in the queue
a. Copy the queue into a results list in case this is the final level.
b. For all nodes in the queue:
i. Pop the node.
ii. Decrement its neighbors' degrees.
iii. Add neighbors to the queue for the next level (not the
current queue) if they now have degree 1.
4. Return the results list. 65
I-mplementation (page 1)
class Solution {
class Solution:
List<List<Integer>> graph = newArrayList<>();
def findMinHeightTrees(self, n: int,
int[] degree;
edges: List[List[int]]) -> List[int]:
graph = defaultdict(list) public List<Integer> findMinHeightTrees(int n,
degree = [0] * n int[][] edges) {
# Build adjacency lists and degree list. // Build adjacency lists and degree array.
for edge in edges: for (int i = 0; i < n; i++) {
graph.add(new ArrayList<Integer>());
u, v = edge
}
graph[u].append(v) degree = new int[n];
graph[v].append(u) for (int i = 0; i < edges.length; i++) {
degree[u] += 1 graph.get(edges[i][0]).add(edges[i][1]);
degree[v] += 1 graph.get(edges[i][1]).add(edges[i][0]);
degree[edges[i][0]]++;
degree[edges[i][1]]++;
}
66
I-mplementation (page 2)
// Build a queue for BFS.
# Build a queue for BFS.
Queue<Integer> queue = new ArrayDeque<Integer>();
# Add all leaves to the queue.
queue = deque(i for i in range(n) if degree[i] == 1)
// Add all leaves to the queue.
for (int i = 0; i < n; i++) {
# Create a list to hold the results.
if (degree[i] == 1) {
results = []
queue.add(i);
}
}
67
I-mplementation (page 3)
while (!queue.isEmpty()) {
while queue:
// Save the list of nodes at the current level,
# Save the list of nodes at the current level,
// in case this is the final level.
# in case this is the final level.
results = new ArrayList<>(queue);
results = queue.copy()
https://leetcode.com/problems/minimum-height-trees/solutions/3811927/clear-commented-java-python3-solution-with-explanation/ 68
E-valuate
Let V be the number of nodes and E the number of edges.
69
5 minutes
Wrap Up
Exit Ticket 45 sec
71
Exit Ticket
72
Exit Ticket 45 sec
73
Exit Ticket
74
Before you leave...
❒ Complete the Session Survey [5 min]
○ Course: [TIP103-S1] Advanced Technical Interview Prep (Section 1?)
○ Session #1
❒ Complete by 11:59pm PDT the night before next week's first session
○ HackerRank assessment → Link on the assignment tab
○ Warm up for next unit → Link on the warmup tab
75