Skip to content

Commit 554e092

Browse files
authored
Merge pull request #150 from bhyun-kim/main
[bhyun-kim, 김병현] Week 9 Solutions
2 parents 8cf1244 + 06f7245 commit 554e092

File tree

5 files changed

+295
-0
lines changed

5 files changed

+295
-0
lines changed

clone-graph/bhyun-kim.py

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"""
2+
133. Clone Graph
3+
https://leetcode.com/problems/clone-graph/
4+
5+
Solution:
6+
To clone a graph, we can use a depth-first search (DFS) to explore all nodes and their neighbors.
7+
We can create a helper function that takes a node and returns its clone.
8+
9+
- We can use a dictionary to map old nodes to new nodes.
10+
- We can create a helper function to clone a node and its neighbors.
11+
- If the node has already been cloned, we return the clone.
12+
- Otherwise, we create a new clone and add it to the dictionary.
13+
- We clone all neighbors of the node recursively.
14+
- We return the clone.
15+
- We start the DFS from the given node and return the clone.
16+
17+
Time complexity: O(n+m)
18+
- n is the number of nodes in the graph.
19+
- m is the number of edges in the graph.
20+
- We explore all nodes and edges once.
21+
22+
Space complexity: O(n)
23+
- We use a dictionary to keep track of the mapping between old nodes and new nodes.
24+
- The maximum depth of the recursive call stack is the number of nodes in the graph.
25+
"""
26+
27+
28+
# Definition for a Node.
29+
class Node:
30+
def __init__(self, val=0, neighbors=None):
31+
self.val = val
32+
self.neighbors = neighbors if neighbors is not None else []
33+
34+
35+
from typing import Optional
36+
37+
38+
class Solution:
39+
def cloneGraph(self, node: Optional["Node"]) -> Optional["Node"]:
40+
if not node:
41+
return None
42+
43+
old_to_new = {}
44+
45+
def dfs(node):
46+
if node in old_to_new:
47+
return old_to_new[node]
48+
49+
clone = Node(node.val)
50+
old_to_new[node] = clone
51+
52+
for neighbor in node.neighbors:
53+
clone.neighbors.append(dfs(neighbor))
54+
55+
return clone
56+
57+
return dfs(node)

course-schedule/bhyun-kim.py

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""
2+
207. Course Schedule
3+
https://leetcode.com/problems/course-schedule/
4+
5+
Solution:
6+
If there is a cycle in the graph, it is impossible to finish all courses.
7+
We can detect a cycle by using DFS and keeping track of the state of each node.
8+
9+
- We can create an adjacency list to represent the graph.
10+
- We can use a state array to keep track of the state of each node.
11+
- 0: unvisited, 1: visiting, 2: visited
12+
- We can create a helper function to check if there is a cycle starting from a node.
13+
- If the node is being visited, we have a cycle.
14+
- If the node has been visited, there is no cycle.
15+
- If not, we mark the node as visiting and explore its neighbors.
16+
- After exploring all neighbors, we mark the node as visited.
17+
- We can iterate through all nodes and check for cycles.
18+
- If we find a cycle, we return False.
19+
- If no cycle is found, we return True.
20+
21+
Time complexity: O(m + n)
22+
- m is the number of prerequisites.
23+
- n is the number of courses.
24+
- We explore all prerequisites and courses once.
25+
26+
Space complexity: O(m + n)
27+
- We use an adjacency list to represent the graph.
28+
- We use a state array to keep track of the state of each node.
29+
"""
30+
31+
from collections import defaultdict
32+
from typing import List
33+
34+
35+
class Solution:
36+
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
37+
adj_list = defaultdict(list)
38+
for dest, src in prerequisites:
39+
adj_list[src].append(dest)
40+
41+
# State: 0 = unvisited, 1 = visiting, 2 = visited
42+
state = [0] * numCourses
43+
44+
def has_cycle(v):
45+
if state[v] == 1: # Node is being visited, so we have a cycle
46+
return True
47+
if state[v] == 2: # Node has been visited, no cycle here
48+
return False
49+
50+
state[v] = 1
51+
for neighbor in adj_list[v]:
52+
if has_cycle(neighbor):
53+
return True
54+
55+
state[v] = 2
56+
return False
57+
58+
for course in range(numCourses):
59+
if state[course] == 0:
60+
if has_cycle(course):
61+
return False
62+
63+
return True
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""
2+
211. Design Add and Search Words Data Structure
3+
https://leetcode.com/problems/design-add-and-search-words-data-structure/
4+
5+
Solution:
6+
To solve this problem, we can use a trie data structure to store the words.
7+
We can create a TrieNode class to represent each node in the trie.
8+
9+
In addWord, we can iterate through each character in the word and create nodes as needed.
10+
We mark the end of the word by setting is_end_of_word to True.
11+
12+
In search, we can use a depth-first search (DFS) to explore all possible paths.
13+
If we encounter a '.', we explore all children of the current node.
14+
If we find a mismatch or reach the end of the word, we return False.
15+
16+
17+
Time complexity:
18+
- addWord: O(m)
19+
- m is the length of the word.
20+
- We iterate through each character in the word once.
21+
- search: O(n * 26^l)
22+
- n is the number of nodes in the trie.
23+
- l is the length of the word.
24+
- We explore all possible paths in the trie.
25+
- The worst-case time complexity is O(n * 26^l) when all nodes have 26 children.
26+
27+
Space complexity: O(n)
28+
- n is the number of nodes in the trie.
29+
- We use a trie data structure to store the words.
30+
"""
31+
32+
33+
class TrieNode:
34+
def __init__(self):
35+
self.children = {}
36+
self.is_end_of_word = False
37+
38+
39+
class WordDictionary:
40+
def __init__(self):
41+
self.root = TrieNode()
42+
43+
def addWord(self, word):
44+
node = self.root
45+
for char in word:
46+
if char not in node.children:
47+
node.children[char] = TrieNode()
48+
node = node.children[char]
49+
node.is_end_of_word = True
50+
51+
def search(self, word):
52+
def dfs(j, node):
53+
for i in range(j, len(word)):
54+
char = word[i]
55+
if char == ".":
56+
for child in node.children.values():
57+
if dfs(i + 1, child):
58+
return True
59+
return False
60+
else:
61+
if char not in node.children:
62+
return False
63+
node = node.children[char]
64+
return node.is_end_of_word
65+
66+
return dfs(0, self.root)

number-of-islands/bhyun-kim.py

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""
2+
200. Number of Islands
3+
https://leetcode.com/problems/number-of-islands/
4+
5+
Solution:
6+
To solve this problem, we can use a depth-first search (DFS) to explore all connected land cells.
7+
We can create a helper function that takes the current cell and marks it as visited.
8+
9+
- We can iterate through all cells in the grid.
10+
- If the current cell is land, we explore all connected land cells using DFS.
11+
- We mark all connected land cells as visited.
12+
- We increment the island count by 1.
13+
14+
Time complexity: O(m x n)
15+
- m and n are the dimensions of the grid.
16+
- We explore all cells in the grid once.
17+
18+
Space complexity: O(m x n)
19+
- We use a recursive call stack to explore all connected land cells.
20+
- The maximum depth of the call stack is the number of cells in the grid.
21+
22+
"""
23+
24+
from typing import List
25+
26+
27+
class Solution:
28+
def numIslands(self, grid: List[List[str]]) -> int:
29+
if not grid:
30+
return 0
31+
32+
m, n = len(grid), len(grid[0])
33+
34+
island_count = 0
35+
36+
def dfs(i, j):
37+
if i < 0 or i >= m or j < 0 or j >= n or grid[i][j] != "1":
38+
return
39+
grid[i][j] = "#"
40+
41+
dfs(i - 1, j)
42+
dfs(i + 1, j)
43+
dfs(i, j - 1)
44+
dfs(i, j + 1)
45+
46+
for i in range(m):
47+
for j in range(n):
48+
if grid[i][j] == "1":
49+
dfs(i, j)
50+
island_count += 1
51+
52+
return island_count
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"""
2+
417. Pacific Atlantic Water Flow
3+
https://leetcode.com/problems/pacific-atlantic-water-flow/
4+
5+
Solution:
6+
To solve this problem, we can use depth-first search (DFS) to explore all possible paths starting from each cell.
7+
We can create a helper function that takes the current cell and marks it as reachable.
8+
9+
- We can create two sets to store the cells that are reachable from the Pacific and Atlantic oceans.
10+
- We can start DFS from the cells on the borders of the Pacific and Atlantic oceans.
11+
- We can find the intersection of the two sets to get the cells that are reachable from both oceans.
12+
13+
14+
Time complexity: O(m x n)
15+
- m and n are the dimensions of the grid.
16+
- We explore all cells in the grid once.
17+
18+
Space complexity: O(m x n)
19+
- We use two sets to store the reachable cells from the Pacific and Atlantic oceans.
20+
- The maximum size of the sets is the number of cells in the grid.
21+
"""
22+
23+
24+
from typing import List
25+
26+
27+
class Solution:
28+
def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]:
29+
if not heights or not heights[0]:
30+
return []
31+
32+
def dfs(matrix, reachable, x, y):
33+
directions = [(1, 0), (-1, 0), (0, 1), (0, -1)]
34+
reachable.add((x, y))
35+
for dx, dy in directions:
36+
nx, ny = x + dx, y + dy
37+
if (
38+
0 <= nx < m
39+
and 0 <= ny < n
40+
and (nx, ny) not in reachable
41+
and matrix[nx][ny] >= matrix[x][y]
42+
):
43+
dfs(matrix, reachable, nx, ny)
44+
45+
m, n = len(heights), len(heights[0])
46+
pacific_reachable = set()
47+
atlantic_reachable = set()
48+
49+
for i in range(m):
50+
dfs(heights, pacific_reachable, i, 0)
51+
dfs(heights, atlantic_reachable, i, n - 1)
52+
53+
for j in range(n):
54+
dfs(heights, pacific_reachable, 0, j)
55+
dfs(heights, atlantic_reachable, m - 1, j)
56+
57+
return list(pacific_reachable & atlantic_reachable)

0 commit comments

Comments
 (0)