Skip to content

Commit 0633719

Browse files
authored
Merge pull request #554 from lymchgmk/feat/week11
[EGON] Week11 Solutions
2 parents 9bf35da + 0608217 commit 0633719

File tree

5 files changed

+367
-0
lines changed

5 files changed

+367
-0
lines changed

binary-tree-maximum-path-sum/EGON.py

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
from typing import Optional
2+
from unittest import TestCase, main
3+
4+
5+
# Definition for a binary tree node.
6+
class TreeNode:
7+
def __init__(self, val=0, left=None, right=None):
8+
self.val = val
9+
self.left = left
10+
self.right = right
11+
12+
13+
class Solution:
14+
def maxPathSum(self, root: Optional[TreeNode]) -> int:
15+
return self.solve_dfs(root)
16+
17+
"""
18+
Runtime: 11 ms (Beats 98.62%)
19+
Time Complexity: O(n)
20+
> dfs를 통해 모든 node를 방문하므로 O(n)
21+
22+
Memory: 22.10 MB (Beats 10.70%)
23+
Space Complexity: O(n)
24+
- dfs 재귀 호출 스택의 깊이는 이진트리가 최악으로 편향된 경우 O(n), upper bound
25+
- 나머지 변수는 O(1)
26+
> O(n), upper bound
27+
"""
28+
def solve_dfs(self, root: Optional[TreeNode]) -> int:
29+
max_path_sum = float('-inf')
30+
31+
def dfs(node: Optional[TreeNode]) -> int:
32+
nonlocal max_path_sum
33+
34+
if not node:
35+
return 0
36+
37+
max_left = max(dfs(node.left), 0)
38+
max_right = max(dfs(node.right), 0)
39+
max_path_sum = max(max_path_sum, node.val + max_left + max_right)
40+
41+
return node.val + max(max_left, max_right)
42+
43+
dfs(root)
44+
45+
return max_path_sum
46+
47+
48+
class _LeetCodeTestCases(TestCase):
49+
def test_1(self):
50+
root = TreeNode(-10)
51+
node_1 = TreeNode(9)
52+
node_2 = TreeNode(20)
53+
node_3 = TreeNode(15)
54+
node_4 = TreeNode(7)
55+
node_2.left = node_3
56+
node_2.right = node_4
57+
root.left = node_1
58+
root.right = node_2
59+
60+
# root = [-10, 9, 20, None, None, 15, 7]
61+
output = 42
62+
63+
self.assertEqual(Solution().maxPathSum(root), output)
64+
65+
66+
if __name__ == '__main__':
67+
main()

graph-valid-tree/EGON.py

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
from typing import List
2+
from unittest import TestCase, main
3+
4+
5+
class ListNode:
6+
def __init__(self, val=0, next=None):
7+
self.val = val
8+
self.next = next
9+
10+
11+
class Solution:
12+
def valid_tree(self, n: int, edges: List[List[int]]) -> bool:
13+
return self.solve_union_find(n, edges)
14+
15+
"""
16+
LintCode 로그인이 안되어서 https://neetcode.io/problems/valid-tree에서 실행시키고 통과만 확인했습니다.
17+
18+
Runtime: ? ms (Beats ?%)
19+
Time Complexity: O(max(m, n))
20+
- UnionFind의 parent 생성에 O(n)
21+
- edges 조회에 O(m)
22+
- Union-find 알고리즘의 union을 매 조회마다 사용하므로, * O(α(n)) (α는 아커만 함수의 역함수)
23+
- UnionFind의 모든 노드가 같은 부모, 즉 모두 연결되어 있는지 확인하기 위해, n번 find에 O(n * α(n))
24+
> O(n) + O(m * α(n)) + O(n * α(n)) ~= O(max(m, n) * α(n)) ~= O(max(m, n)) (∵ α(n) ~= C)
25+
26+
Memory: ? MB (Beats ?%)
27+
Space Complexity: O(n)
28+
- UnionFind의 parent와 rank가 크기가 n인 리스트이므로, O(n) + O(n)
29+
> O(n) + O(n) ~= O(n)
30+
"""
31+
def solve_union_find(self, n: int, edges: List[List[int]]) -> bool:
32+
33+
class UnionFind:
34+
def __init__(self, size: int):
35+
self.parent = [i for i in range(size)]
36+
self.rank = [1] * size
37+
38+
def union(self, first: int, second: int) -> bool:
39+
first_parent, second_parent = self.find(first), self.find(second)
40+
if first_parent == second_parent:
41+
return False
42+
43+
if self.rank[first_parent] > self.rank[second_parent]:
44+
self.parent[second_parent] = first_parent
45+
elif self.rank[first_parent] < self.rank[second_parent]:
46+
self.parent[first_parent] = second_parent
47+
else:
48+
self.parent[second_parent] = first_parent
49+
self.rank[first_parent] += 1
50+
51+
return True
52+
53+
def find(self, node: int):
54+
if self.parent[node] != node:
55+
self.parent[node] = self.find(self.parent[node])
56+
57+
return self.parent[node]
58+
59+
unionFind = UnionFind(size=n)
60+
for first, second in edges:
61+
is_cycle = unionFind.union(first, second) is False
62+
if is_cycle:
63+
return False
64+
65+
root = unionFind.find(0)
66+
for i in range(1, n):
67+
if unionFind.find(i) != root:
68+
return False
69+
70+
return True
71+
72+
73+
class _LeetCodeTestCases(TestCase):
74+
def test_1(self):
75+
n = 5
76+
edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]]
77+
output = False
78+
79+
self.assertEqual(Solution().valid_tree(n, edges), output)
80+
81+
82+
if __name__ == '__main__':
83+
main()

insert-interval/EGON.py

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from bisect import bisect_left
2+
from typing import List
3+
from unittest import TestCase, main
4+
5+
6+
class Solution:
7+
def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
8+
return self.solve(intervals, newInterval)
9+
10+
"""
11+
Runtime: 1 ms (Beats 95.76%)
12+
Time Complexity: O(n)
13+
> intervals의 전체를 선형적으로 조회하므로 O(n), 그 외의 append등의 연산들은 O(1)이므로 무시
14+
15+
Memory: 18.70 MB (Beats 99.60%)
16+
Space Complexity: O(n)
17+
> result의 크기는 intervals와 newInterval이 하나도 겹치지 않는 경우, 최대 n + 1이므로, O(n + 1) ~= O(n)
18+
"""
19+
def solve(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
20+
if not intervals:
21+
return [newInterval]
22+
23+
result = []
24+
new_s, new_e = newInterval
25+
for interval in intervals:
26+
s, e = interval
27+
if e < new_s:
28+
result.append(interval)
29+
elif new_e < s:
30+
if new_s != -1 and new_e != -1:
31+
result.append([new_s, new_e])
32+
new_s = new_e = -1
33+
34+
result.append(interval)
35+
else:
36+
new_s = min(new_s, s)
37+
new_e = max(new_e, e)
38+
39+
if new_s != -1 and new_e != -1:
40+
result.append([new_s, new_e])
41+
42+
return result
43+
44+
45+
class _LeetCodeTestCases(TestCase):
46+
def test_1(self):
47+
intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]]
48+
newInterval = [4,8]
49+
output = [[1,2],[3,10],[12,16]]
50+
self.assertEqual(Solution().insert(intervals, newInterval), output)
51+
52+
def test_2(self):
53+
intervals = [[1,2]]
54+
newInterval = [3,4]
55+
output = [[1,2], [3,4]]
56+
self.assertEqual(Solution().insert(intervals, newInterval), output)
57+
58+
def test_3(self):
59+
intervals = [[1,3], [5,6]]
60+
newInterval = [4,5]
61+
output = [[1,3], [4,6]]
62+
self.assertEqual(Solution().insert(intervals, newInterval), output)
63+
64+
65+
if __name__ == '__main__':
66+
main()

maximum-depth-of-binary-tree/EGON.py

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
from typing import Optional
2+
from unittest import TestCase, main
3+
4+
5+
# Definition for a binary tree node.
6+
class TreeNode:
7+
def __init__(self, val=0, left=None, right=None):
8+
self.val = val
9+
self.left = left
10+
self.right = right
11+
12+
13+
class Solution:
14+
def maxDepth(self, root: Optional[TreeNode]) -> int:
15+
return self.solve_dfs_iterable(root)
16+
17+
"""
18+
Runtime: 0 ms (Beats 100.00%)
19+
Time Complexity: O(n)
20+
> 트리의 모든 노드의 갯수를 n개라고 하면, 트리의 모든 노드를 stack에 넣어 조회하므로 O(n)
21+
22+
Memory: 17.75 MB (Beats 21.97%)
23+
Space Complexity: O(n)
24+
> 최악의 경우 트리의 최대 길이가 n인 경우이므로, stack의 최대 크기가 n에 비례하므로 O(n), upper bound
25+
"""
26+
def solve_dfs_iterable(self, root: Optional[TreeNode]) -> int:
27+
max_depth = 0
28+
stack = [(root, 0)]
29+
while stack:
30+
curr_node, curr_depth = stack.pop()
31+
if curr_node is None:
32+
continue
33+
34+
if curr_node.left is None and curr_node.right is None:
35+
max_depth = max(max_depth, curr_depth + 1)
36+
continue
37+
38+
if curr_node.left:
39+
stack.append((curr_node.left, curr_depth + 1))
40+
if curr_node.right:
41+
stack.append((curr_node.right, curr_depth + 1))
42+
43+
return max_depth
44+
45+
46+
"""
47+
Runtime: 0 ms (Beats 100.00%)
48+
Time Complexity: O(n)
49+
50+
Memory: 17.90 MB (Beats 9.05%)
51+
Space Complexity: O(n)
52+
"""
53+
def solve_dfs_recursive(self, root: Optional[TreeNode]) -> int:
54+
max_depth = 0
55+
56+
def dfs(node: Optional[TreeNode], depth: int):
57+
nonlocal max_depth
58+
59+
if not node:
60+
return max_depth
61+
62+
if node.left is None and node.right is None:
63+
max_depth = max(max_depth, depth + 1)
64+
return
65+
66+
dfs(node.left, depth + 1)
67+
dfs(node.right, depth + 1)
68+
69+
dfs(root, 0)
70+
71+
return max_depth
72+
73+
74+
class _LeetCodeTestCases(TestCase):
75+
def test_1(self):
76+
self.assertEqual(True, True)
77+
78+
79+
if __name__ == '__main__':
80+
main()

reorder-list/EGON.py

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from heapq import heappush, heappop
2+
from typing import List, Optional
3+
from unittest import TestCase, main
4+
5+
6+
class ListNode:
7+
def __init__(self, val=0, next=None):
8+
self.val = val
9+
self.next = next
10+
11+
12+
class Solution:
13+
def reorderList(self, head: Optional[ListNode]) -> None:
14+
return self.solve(head)
15+
16+
"""
17+
Runtime: 15 ms (Beats 88.30%)
18+
Time Complexity: O(n)
19+
- 역방향 링크드 리스트인 backward를 생성하는데, 원본 링크드 리스트의 모든 노드를 조회하는데 O(n)
20+
- reorder하는데 원본 링크드 리스트의 모든 노드의 길이만큼 backward와 forward의 노드들을 조회하는데 O(n)
21+
> O(n) + O(n) = 2 * O(n) ~= O(n)
22+
23+
Memory: 23.20 MB (Beats 88.27%)
24+
Space Complexity: O(n)
25+
> 역방향 링크드 리스트인 backward를 생성하는데, backward의 길이는 원본 링크드 리스트의 길이와 같으므로 O(n)
26+
"""
27+
def solve(self, head: Optional[ListNode]) -> None:
28+
backward = ListNode(head.val)
29+
backward_node = head.next
30+
length = 1
31+
while backward_node:
32+
length += 1
33+
temp_node = ListNode(backward_node.val)
34+
temp_node.next = backward
35+
backward = temp_node
36+
backward_node = backward_node.next
37+
38+
node = head
39+
forward = head.next
40+
for i in range(length):
41+
if i == length - 1:
42+
node.next = None
43+
return
44+
45+
if i % 2 == 0:
46+
node.next = backward
47+
backward = backward.next
48+
node = node.next
49+
else:
50+
node.next = forward
51+
forward = forward.next
52+
node = node.next
53+
54+
55+
class _LeetCodeTestCases(TestCase):
56+
def test_1(self):
57+
node_1 = ListNode(1)
58+
node_2 = ListNode(2)
59+
node_3 = ListNode(3)
60+
node_4 = ListNode(4)
61+
node_5 = ListNode(5)
62+
node_1.next = node_2
63+
node_2.next = node_3
64+
node_3.next = node_4
65+
node_4.next = node_5
66+
67+
self.assertEqual(Solution().reorderList(node_1), True)
68+
69+
70+
if __name__ == '__main__':
71+
main()

0 commit comments

Comments
 (0)