Skip to content

Commit fad730e

Browse files
authored
Merge pull request #595 from lymchgmk/feat/week14
[EGON] Week14 Solutions
2 parents d0b74d1 + d0ebb69 commit fad730e

File tree

5 files changed

+388
-0
lines changed

5 files changed

+388
-0
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
from collections import deque
2+
from typing import Optional, List
3+
from unittest import TestCase, main
4+
5+
6+
# Definition for a binary tree node.
7+
class TreeNode:
8+
def __init__(self, val=0, left=None, right=None):
9+
self.val = val
10+
self.left = left
11+
self.right = right
12+
13+
14+
class Solution:
15+
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
16+
return self.solve_dfs(root)
17+
18+
"""
19+
Runtime: 0 ms (Beats 100.00%)
20+
Time Complexity: O(n)
21+
> 각 node를 bfs 방식으로 한 번 씩 조회하므로 O(n)
22+
23+
Memory: 17.42 (Beats 11.96%)
24+
Space Complexity: O(n)
25+
> 최악의 경우, deque에 최대 node의 갯수만큼 저장될 수 있으므로 O(n), upper bound
26+
"""
27+
def solve_bfs(self, root: Optional[TreeNode]) -> List[List[int]]:
28+
if not root:
29+
return []
30+
31+
dq = deque([root])
32+
result = []
33+
while dq:
34+
tmp_result = []
35+
for _ in range(len(dq)):
36+
curr = dq.popleft()
37+
tmp_result.append(curr.val)
38+
39+
if curr.left:
40+
dq.append(curr.left)
41+
if curr.right:
42+
dq.append(curr.right)
43+
44+
result.append(tmp_result)
45+
46+
return result
47+
48+
"""
49+
Runtime: 1 ms (Beats 38.71%)
50+
Time Complexity: O(n)
51+
> 각 node를 dfs 방식으로 한 번 씩 조회하므로 O(n)
52+
53+
Memory: 17.49 (Beats 11.96%)
54+
Space Complexity: O(1)
55+
> 최악의 경우, list에 최대 node의 갯수만큼 저장될 수 있으므로 O(n), upper bound
56+
"""
57+
def solve_dfs(self, root: Optional[TreeNode]) -> List[List[int]]:
58+
if not root:
59+
return []
60+
61+
def dfs(node: TreeNode, depth: int):
62+
if len(result) <= depth:
63+
result.append([node.val])
64+
else:
65+
result[depth].append(node.val)
66+
67+
if node.left:
68+
dfs(node.left, depth + 1)
69+
if node.right:
70+
dfs(node.right, depth + 1)
71+
72+
result = []
73+
dfs(root, 0)
74+
75+
return result
76+
77+
78+
class _LeetCodeTestCases(TestCase):
79+
80+
def test_1(self):
81+
root = TreeNode(3)
82+
node1 = TreeNode(9)
83+
node2 = TreeNode(20)
84+
node3 = TreeNode(15)
85+
node4 = TreeNode(7)
86+
root.left = node1
87+
root.right = node2
88+
node2.left = node3
89+
node2.right = node4
90+
output = [[3], [9, 20], [15, 7]]
91+
self.assertEqual(Solution.levelOrder(Solution(), root), output)
92+
93+
def test_2(self):
94+
root = TreeNode(1)
95+
node1 = TreeNode(2)
96+
node2 = TreeNode(3)
97+
node3 = TreeNode(4)
98+
node4 = TreeNode(5)
99+
root.left = node1
100+
root.right = node2
101+
node1.left = node3
102+
node2.left = node4
103+
output = [[1], [2, 3], [4, 5]]
104+
self.assertEqual(Solution.levelOrder(Solution(), root), output)
105+
106+
107+
if __name__ == '__main__':
108+
main()

house-robber-ii/EGON.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from collections import deque
2+
from typing import List
3+
from unittest import TestCase, main
4+
5+
6+
class Solution:
7+
def rob(self, nums: List[int]) -> int:
8+
return self.solve_dp(nums)
9+
10+
"""
11+
Runtime: 0 ms (Beats 100.00%)
12+
Time Complexity: O(n)
13+
- 인덱스가 0인 곳을 도둑질한 경우에 대한 dp1에서, 인덱스 2부터 n-2까지 조회하므로 O(n - 3)
14+
- 각 조회마다 2항 max 연산을 2회씩 하므로 * O(2 * 2)
15+
- 인덱스가 0인 곳을 도둑질하지 않는 경우에 대한 dp2에서, 인덱스 2부터 n-1까지 조회하므로 O(n - 2)
16+
- 각 조회마다 2항 max 연산을 2회씩 하므로 * O(2 * 2)
17+
- 그 외에 정해진 횟수의 max 연산들은 무시, O(C)
18+
> O(n - 3) * O(2 * 2) + O(n - 4) * O(2 * 2) + O(C) ~= O(n) * O(4) ~= O(n)
19+
20+
Memory: 16.59 (Beats 62.16%)
21+
Space Complexity: O(n)
22+
- dp1과 dp2가 각각 nums의 길이와 같으므로 O(n * 2)
23+
> O(n * 2) ~= O(n)
24+
"""
25+
def solve_dp(self, nums: List[int]) -> int:
26+
if len(nums) <= 3:
27+
return max(nums)
28+
29+
dp1 = [0] * len(nums)
30+
dp1[0], dp1[1] = nums[0], max(nums[0], nums[1])
31+
max_dp1 = max(dp1[0], dp1[1])
32+
for i in range(2, len(nums) - 1):
33+
dp1[i] = max(dp1[i - 1], dp1[i - 2] + nums[i])
34+
max_dp1 = max(max_dp1, dp1[i])
35+
36+
dp2 = [0] * len(nums)
37+
dp2[0], dp2[1] = 0, nums[1]
38+
max_dp2 = max(dp2[0], dp2[1])
39+
for i in range(2, len(nums)):
40+
dp2[i] = max(dp2[i - 1], dp2[i - 2] + nums[i])
41+
max_dp2 = max(max_dp2, dp2[i])
42+
43+
return max(max_dp1, max_dp2)
44+
45+
46+
class _LeetCodeTestCases(TestCase):
47+
48+
def test_1(self):
49+
nums = [2, 3, 2]
50+
output = 3
51+
self.assertEqual(Solution.rob(Solution(), nums), output)
52+
53+
def test_2(self):
54+
nums = [1, 2, 3, 1]
55+
output = 4
56+
self.assertEqual(Solution.rob(Solution(), nums), output)
57+
58+
59+
if __name__ == '__main__':
60+
main()

meeting-rooms-ii/EGON.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
from typing import List
2+
from unittest import TestCase, main
3+
4+
5+
# Definition of Interval:
6+
class Interval(object):
7+
def __init__(self, start, end):
8+
self.start = start
9+
self.end = end
10+
11+
12+
class Solution:
13+
def minMeetingRooms(self, intervals: List[Interval]) -> int:
14+
return self.solve_two_pointer(intervals)
15+
16+
"""
17+
LintCode 로그인이 안되어서 https://neetcode.io/problems/meeting-schedule-ii 에서 실행시키고 통과만 확인했습니다.
18+
19+
Runtime: ? ms (Beats ?%)
20+
Time Complexity: O(n log n)
21+
- intervals의 길이를 n이라 하면, starts를 정렬하는데 O(n log n)
22+
- ends를 정렬하는데 O(n log n)
23+
- intervals를 인덱스를 이용해 전체 조회하는데 O(n)
24+
> O(n log n) * 2 + O(n) ~= O(n log n)
25+
26+
Memory: ? MB (Beats ?%)
27+
Space Complexity: O(n)
28+
- starts와 ends의 크기는 intervals와 같으므로 O(n)
29+
- 포인터로 index를 사용했으므로 O(1)
30+
> O(n) + O(1) ~= O(n)
31+
"""
32+
def solve_two_pointer(self, intervals: List[Interval]) -> int:
33+
if not intervals:
34+
return 0
35+
36+
starts, ends = sorted([i.start for i in intervals]), sorted([i.end for i in intervals])
37+
start_idx, end_idx = 0, 0
38+
schedule_count = 0
39+
while start_idx < len(intervals):
40+
if starts[start_idx] < ends[end_idx]:
41+
schedule_count += 1
42+
start_idx += 1
43+
else:
44+
end_idx += 1
45+
start_idx += 1
46+
47+
return schedule_count
48+
49+
50+
class _LeetCodeTestCases(TestCase):
51+
def test_1(self):
52+
intervals = [Interval(0,40), Interval(5,10), Interval(15,20)]
53+
output = 2
54+
self.assertEqual(Solution().minMeetingRooms(intervals), output)
55+
56+
def test_2(self):
57+
intervals = [Interval(4, 9)]
58+
output = 1
59+
self.assertEqual(Solution().minMeetingRooms(intervals), output)
60+
61+
def test_3(self):
62+
intervals = [
63+
Interval(1, 5),
64+
Interval(2, 6),
65+
Interval(3, 7),
66+
Interval(4, 8),
67+
Interval(5, 9),
68+
]
69+
output = 4
70+
self.assertEqual(Solution().minMeetingRooms(intervals), output)
71+
72+
73+
if __name__ == '__main__':
74+
main()

reverse-bits/EGON.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from unittest import TestCase, main
2+
3+
4+
class Solution:
5+
def reverseBits(self, n: int) -> int:
6+
return self.solve(n)
7+
8+
"""
9+
Runtime: 32 ms (Beats 80.50%)
10+
Time Complexity: O(1)
11+
- n을 str로 변환하는데, n은 32 bit 정수이므로 O(32), upper bound
12+
- zfill로 문자열의 길이를 32로 맞추는데, O(32), upper bound
13+
- 문자열을 뒤집는데 마찬가지로, O(32), upper bound
14+
- 뒤집은 문자열을 정수로 변환하는데 문자열에 비례하며 이 길이는 최대 32이므로, O(32), upper bound
15+
> O(32) * O(32) * O(32) * O(32) ~= O(1)
16+
17+
Memory: 16.50 (Beats 64.72%)
18+
Space Complexity: O(1)
19+
- 각 단계마다 최대 길이가 32인 문자열이 임시로 저장되므로 O(32) * 4
20+
> O(32) * 4 ~= O(1)
21+
"""
22+
def solve(self, n: int) -> int:
23+
return int(str(n).zfill(32)[::-1], 2)
24+
25+
26+
class _LeetCodeTestCases(TestCase):
27+
def test_1(self):
28+
n = int("00000010100101000001111010011100")
29+
output = 964176192
30+
self.assertEqual(Solution.reverseBits(Solution(), n), output)
31+
32+
33+
if __name__ == '__main__':
34+
main()

word-search-ii/EGON.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
from typing import List
2+
from unittest import TestCase, main
3+
4+
5+
class Node:
6+
7+
def __init__(self, key, data=None):
8+
self.key = key
9+
self.data = data
10+
self.children = {}
11+
12+
13+
class Trie:
14+
15+
def __init__(self):
16+
self.root = Node(None)
17+
18+
def insert(self, word: str) -> None:
19+
curr_node = self.root
20+
for char in word:
21+
if char not in curr_node.children:
22+
curr_node.children[char] = Node(char)
23+
24+
curr_node = curr_node.children[char]
25+
26+
curr_node.data = word
27+
28+
29+
class Solution:
30+
def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
31+
return self.solve_trie_dfs(board, words)
32+
33+
"""
34+
* Constraints:
35+
1. m == board.length
36+
2. n == board[i].length
37+
3. 1 <= m, n <= 12
38+
4. board[i][j] is a lowercase English letter.
39+
5. 1 <= words.length <= 3 * 104
40+
6. 1 <= words[i].length <= 10
41+
7. words[i] consists of lowercase English letters.
42+
8. All the strings of words are unique.
43+
44+
Runtime: 6439 ms (Beats 26.38%)
45+
Time Complexity: O(R * C * (4 ^ max L))
46+
- word의 최대 길이를 max L, words의 길이를 K라 하면, trie에 words를 모두 insert하는데 O(max L * K), upper bound
47+
- board의 각 grid에서 조회하는데 O(R * C)
48+
- grid마다 dfs 호출하는데, dfs의 방향은 4곳이고, 호출 스택의 최대 깊이는 max L 이므로, * O(4 ^ max L)
49+
> O(max L * K) + O(R * C) * O(4 ^ max L) ~= O(R * C * (4 ^ max L))
50+
51+
Memory: 19.04 MB (Beats 20.79%)
52+
Space Complexity: O(max L * K)
53+
- trie의 공간 복잡도는 O(max L * K), upper bound
54+
- board의 각 grid에서 dfs를 호출하고, dfs 호출 스택의 최대 깊이는 max L 이므로 O(max L)
55+
- result의 최대 크기는 words의 길이와 같으므로 O(K), upper bound
56+
> O(max L * K) + O(max L) + O(K) ~= O(max L * K)
57+
"""
58+
def solve_trie_dfs(self, board: List[List[str]], words: List[str]) -> List[str]:
59+
MAX_R, MAX_C = len(board), len(board[0])
60+
DIRS = [(0, 1), (1, 0), (0, -1), (-1, 0)]
61+
62+
trie = Trie()
63+
for word in words:
64+
trie.insert(word)
65+
66+
def dfs(curr: Node, r: int, c: int, path: str):
67+
nonlocal result
68+
69+
if not (0 <= r < MAX_R and 0 <= c < MAX_C):
70+
return
71+
72+
if board[r][c] == "#":
73+
return
74+
75+
char = board[r][c]
76+
if char not in curr.children:
77+
return
78+
79+
post = curr.children[char]
80+
if post.data:
81+
result.add(post.data)
82+
83+
board[r][c] = "#"
84+
for dir_r, dir_c in DIRS:
85+
dfs(post, r + dir_r, c + dir_c, path + char)
86+
board[r][c] = char
87+
88+
result = set()
89+
for r in range(MAX_R):
90+
for c in range(MAX_C):
91+
if board[r][c] in trie.root.children:
92+
dfs(trie.root, r, c, "")
93+
94+
return list(result)
95+
96+
97+
class _LeetCodeTestCases(TestCase):
98+
def test_1(self):
99+
board = [["o", "a", "a", "n"], ["e", "t", "a", "e"], ["i", "h", "k", "r"], ["i", "f", "l", "v"]]
100+
words = ["oath","pea","eat","rain"]
101+
output = ["eat","oath"]
102+
self.assertEqual(Solution.findWords(Solution(), board, words), output)
103+
104+
def test_2(self):
105+
board = [["a","b"],["c","d"]]
106+
words = ["abcb"]
107+
output = []
108+
self.assertEqual(Solution.findWords(Solution(), board, words), output)
109+
110+
111+
if __name__ == '__main__':
112+
main()

0 commit comments

Comments
 (0)