Skip to content

Commit

Permalink
added new problems
Browse files Browse the repository at this point in the history
  • Loading branch information
romarowski committed Feb 15, 2024
1 parent 7733f22 commit 960d70d
Show file tree
Hide file tree
Showing 7 changed files with 303 additions and 0 deletions.
103 changes: 103 additions & 0 deletions 04-graph.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
## Graph

### Depth-first search

**Recursive**
```python
for node in graph:
dfs(node, graph)

def dfs(node, graph):
for neighbor in graph[node]:
dfs(neighbor, graph)
```

### Minimum island problem

**Statement**: Given a 2D grid of Land and Water, find the smallest island in the grid. An island is defined as land connected horizontally or vertically.

```python
grid = [
["W", "L", "W", "W", "W"],
["W", "L", "W", "W", "W"],
["W", "W", "W", "L", "W"],
["W", "W", "L", "L", "W"],
["L", "W", "W", "L", "L"],
["L", "L", "W", "W", "W"],
]
minimum_island(grid) # 2
```
**Approach**:
1. Iterate through the grid with nested ```for``` loops.
2. On any land cell, perform a **depth-first search** to find the size of the island.
3. Keep track of the visited cells to avoid infinite loops.

### Closest carrot problem

**Statement**: Given a 2D grid of Open Spaces "O"s, Walls "X"s, and Carrots "C"s, find the closest carrot from a given initial position.

```python
grid = [
['O', 'O', 'O', 'O', 'O'],
['O', 'X', 'O', 'O', 'O'],
['O', 'X', 'X', 'O', 'O'],
['O', 'X', 'C', 'O', 'O'],
['O', 'X', 'X', 'O', 'O'],
['C', 'O', 'O', 'O', 'O'],
]

closest_carrot(grid, 1, 2) # -> 4
```
**Approach**:
1. Use a **breadth-first search** to find the shortest path from the initial position to the closest carrot. **BFS** will explore all directions equally, so it will find the shortest path.
2. Keep track of the visited cells to avoid infinite loops.
3. Keep track of the distance to the origin by adding 1 to a ```distance``` variable.
4. Return the ```distance``` when the carrot is found.

### Longest path problem

**Statement**: Givenn an adjacency list for a **directed acyclic graph** (DAG), find the longest path (number of edges) in the graph.


<table>
<tr>
<td>

```python
graph = {
'a': ['c', 'b'],
'b': ['c'],
'c': []
}
longest_path(graph) # -> 2
```

</td>
<td style="text-align: center;">


```mermaid
graph TD
a --> c
a --> b
b --> c
```

</td>
</tr>
</table>


**Approach**:
1. Find the terminal nodes of the graph. A terminal node is a node with no outgoing edges.
- Each terminal node has the potential to be the start of the longest path.
- We need to assign a distance to each node, in particular a terminal node has a distance of 0 (we are going to count backwards).
2. For each node in the graph, do a **depth-first search**.
3. The base case is an already visited node, in which case we return the distance.
4. The recursive case is to return the maximum distance of the neighbors plus 1.
5. Finally, return the maximum distance found.

**Complexity**:
- Time: O(E) where E is the number of edges in the graph.
- Space: O(N) where N is the number of nodes in the graph.

2 changes: 2 additions & 0 deletions 05-dynamic-programming.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
## Dynamic Programming

Empty file added dynamic_programming/fib.py
Empty file.
57 changes: 57 additions & 0 deletions graph/closest_carrot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""
closest carrot
Write a function, closest_carrot, that takes in a grid, a starting row,
and a starting column. In the grid, 'X's are walls, 'O's are open
spaces, and 'C's are carrots. The function should return a number
representing the length of the shortest path from the starting position
to a carrot. You may move up, down, left, or right, but
cannot pass through walls (X).
If there is no possible path to a carrot, then return -1.
"""

from collections import deque

def closest_carrot(grid, starting_row, starting_col):
visited = set((starting_row, starting_col))

q = deque([])
q.append((starting_row, starting_col, 0))

deltas = [(1, 0), (-1,0), (0, 1), (0, -1)]

while len(q) > 0:
r, c, dist = q.popleft()

if grid[r][c] == "C":
return dist

for delta in deltas:
neighbor_row = r + delta[0]
neighbor_col = c + delta[1]

inbound_row = 0 <= neighbor_row < len(grid)
inbound_col = 0 <= neighbor_col < len(grid[0])

if inbound_row and inbound_col:
pos = (neighbor_row, neighbor_col)
if pos not in visited:
if grid[neighbor_row][neighbor_col] != "X":
q.append((
neighbor_row,
neighbor_col,
dist+1
))
visited.add(pos)
return -1

grid = [
['O', 'O', 'X', 'O', 'O'],
['O', 'X', 'O', 'X', 'O'],
['O', 'X', 'X', 'O', 'O'],
['O', 'X', 'C', 'O', 'O'],
['O', 'X', 'X', 'O', 'O'],
['C', 'O', 'O', 'O', 'O'],
]

print(closest_carrot(grid, 1, 2)) # -> 4
48 changes: 48 additions & 0 deletions graph/longest_path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""
longest path
Write a function, longest_path, that takes in an adjacency list for a
directed acyclic graph. The function should return the length of the
longest path within the graph. A path may start and end at any two nodes.
The length of a path is considered the number of edges in the path,
not the number of nodes.
"""

def longest_path(graph):
# sounds like a DFS problem
# I need to go to any given node
# and go to the end
# store how many edges I passed to get to that node
visited = dict()
for node in graph:
if graph[node] == []:
visited[node] = 0
for node in graph:
explore(node, graph, visited)

return max(visited.values())

def explore(node, graph, visited):

if node in visited:
return visited[node]


largest = 0
for neighbor in graph[node]:
path = explore(neighbor, graph, visited)
if path > largest:
largest = path
visited[node] = 1 +largest
return visited[node]




graph = {
'a': ['c', 'b'],
'b': ['c'],
'c': []
}

print(longest_path(graph)) # -> 2
61 changes: 61 additions & 0 deletions graph/minimum _island.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
def minimum_island(grid):
visited = set()
n_rows = len(grid)
n_cols = len(grid[0])
min_size = n_rows*n_cols
# need to iterate through every row and column of grid
# start at the top left
r, c = 0, 0
# we need a stack to do a DFS
for r in range (n_rows):
for c in range(n_cols):
# then we need to do a DFS of the grid
if (r, c) in visited:
continue
cell = grid[r][c]
visited.add((r,c))
if cell == "L":
stack = []
stack.append((r,c))
current_size = 0
while len(stack)>0:
#populate the stack
# add the cell above handling limits
r, c = stack.pop()
visited.add((r,c))
current_size+=1
if r > 0:
up = (r-1, c)
if (up not in visited) and "L"==grid[up[0]][up[1]]:
stack.append(up)
# add the cell down, handling out of bounds
if r < n_rows - 1:
down = (r+1, c)
if (down not in visited) and "L"==grid[down[0]][down[1]]:
stack.append(down)
# add left
if c > 0:
left = (r, c-1)
if (left not in visited) and ("L"==grid[left[0]][left[1]]):
stack.append(left)
if c < n_cols -1:
right=(r, c+1)
if (right not in visited) and ("L"==grid[right[0]][right[1]]):
stack.append(right)
if len(stack)==0:
if current_size<min_size:
min_size=current_size

return min_size

if __name__ == "__main__":
grid = [
['W', 'L', 'W', 'W', 'W'],
['W', 'L', 'W', 'W', 'W'],
['W', 'W', 'W', 'L', 'W'],
['W', 'W', 'L', 'L', 'W'],
['L', 'W', 'W', 'L', 'L'],
['L', 'L', 'W', 'W', 'W'],
]

print(minimum_island(grid))
32 changes: 32 additions & 0 deletions graph/semesters_required.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""
semesters required
Write a function, semesters_required, that takes in a number of
courses (n) and a list of prerequisites as arguments. Courses
have ids ranging from 0 through n - 1. A single prerequisite of
(A, B) means that course A must be taken before course B. Return
the minimum number of semesters required to complete all n
courses. There is no limit on how many courses you can take in
a single semester, as long the prerequisites of a course are
satisfied before taking it.
Note that given prerequisite (A, B), you cannot take course A
and course B concurrently in the same semester. You must take
A in some semester before B.
You can assume that it is possible to eventually complete
all courses.
"""

def semesters_required(num_courses, prereqs):
pass # todo


num_courses = 6
prereqs = [
(1, 2),
(2, 4),
(3, 5),
(0, 5),
]
semesters_required(num_courses, prereqs) # -> 3

0 comments on commit 960d70d

Please sign in to comment.