Skip to content

Commit c70fa10

Browse files
authored
Merge pull request #569 from lymchgmk/feat/week12
[EGON] Week12 Solutions
2 parents 0633719 + cfbb952 commit c70fa10

File tree

5 files changed

+347
-0
lines changed
  • merge-intervals
  • number-of-connected-components-in-an-undirected-graph
  • remove-nth-node-from-end-of-list
  • same-tree
  • serialize-and-deserialize-binary-tree

5 files changed

+347
-0
lines changed

merge-intervals/EGON.py

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from typing import List
2+
from unittest import TestCase, main
3+
4+
5+
class Solution:
6+
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
7+
return self.solve_stack(intervals)
8+
9+
"""
10+
Runtime: 8 ms (Beats 49.77%)
11+
Time Complexity:
12+
- intervals의 길이를 n이라 하면, intervals를 시작 지점을 기준으로 정렬하는데 O(n log n)
13+
- intervals를 조회하면서 연산하는데, 내부 연산은 모두 O(1)이므로 O(n)
14+
> O(n log n) + O(n) ~= O(n log n)
15+
16+
Memory: 19.88 MB (Beats: 99.31%)
17+
Space Complexity: O(n)
18+
- intervals는 내부 정렬만 했으므로 추가 메모리 사용 메모리 없음
19+
- 겹치는 구간이 없는 최악의 경우 merged의 크기는 intervals의 크기와 같아지므로, O(n) upper bound
20+
"""
21+
22+
def solve_stack(self, intervals: List[List[int]]) -> List[List[int]]:
23+
intervals.sort()
24+
25+
merged = []
26+
for interval in intervals:
27+
if not merged:
28+
merged.append(interval)
29+
continue
30+
31+
new_start, new_end = interval
32+
_, last_end = merged[-1]
33+
if last_end < new_start:
34+
merged.append(interval)
35+
else:
36+
merged[-1][1] = max(last_end, new_end)
37+
38+
return merged
39+
40+
41+
class _LeetCodeTestCases(TestCase):
42+
43+
def test_1(self):
44+
intervals = [[1, 3], [2, 6], [8, 10], [15, 18]]
45+
output = [[1, 6], [8, 10], [15, 18]]
46+
47+
self.assertEqual(Solution().merge(intervals), output)
48+
49+
def test_2(self):
50+
intervals = [[1, 4], [4, 5]]
51+
output = [[1, 5]]
52+
53+
self.assertEqual(Solution().merge(intervals), output)
54+
55+
56+
if __name__ == '__main__':
57+
main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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 countComponents(self, n: int, edges: List[List[int]]) -> int:
13+
return self.solve_union_find(n, edges)
14+
15+
"""
16+
LintCode 로그인이 안되어서 https://neetcode.io/problems/count-connected-components에서 실행시키고 통과만 확인했습니다.
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+
32+
def solve_union_find(self, n: int, edges: List[List[int]]) -> int:
33+
34+
class UnionFind:
35+
def __init__(self, size: int):
36+
self.parent = [i for i in range(size)]
37+
self.rank = [1] * size
38+
39+
def union(self, first: int, second: int):
40+
first_parent, second_parent = self.find(first), self.find(second)
41+
42+
if self.rank[first_parent] > self.rank[second_parent]:
43+
self.parent[second_parent] = first_parent
44+
elif self.rank[first_parent] < self.rank[second_parent]:
45+
self.parent[first_parent] = second_parent
46+
else:
47+
self.parent[second_parent] = first_parent
48+
self.rank[first_parent] += 1
49+
50+
def find(self, node: int):
51+
if self.parent[node] != node:
52+
self.parent[node] = self.find(self.parent[node])
53+
54+
return self.parent[node]
55+
56+
union_find = UnionFind(size=n)
57+
for first, second in edges:
58+
union_find.union(first, second)
59+
60+
result = set()
61+
for i in range(n):
62+
result.add(union_find.find(i))
63+
64+
return len(result)
65+
66+
67+
class _LeetCodeTestCases(TestCase):
68+
def test_1(self):
69+
n = 5
70+
edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]]
71+
output = False
72+
73+
self.assertEqual(Solution().countComponents(n, edges), output)
74+
75+
76+
if __name__ == '__main__':
77+
main()
+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
from typing import Optional
2+
from unittest import TestCase, main
3+
4+
5+
# Definition for singly-linked list.
6+
class ListNode:
7+
def __init__(self, val=0, next=None):
8+
self.val = val
9+
self.next = next
10+
11+
def values(self) -> [int]:
12+
result = []
13+
node = self
14+
while node:
15+
result.append(node.val)
16+
node = node.next
17+
18+
return result
19+
20+
21+
class Solution:
22+
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
23+
return self.solve_two_pointer(head, n)
24+
"""
25+
Runtime: 0 ms (Beats 100.00%)
26+
Time Complexity: O(n)
27+
> list의 모든 node + dummy node를 탐색하므로 O(n + 1) ~= O(n)
28+
29+
Memory: 16.62 MB (Beats 15.78%)
30+
Space Complexity: O(1)
31+
> 포인터만 사용하므로 O(1)
32+
"""
33+
def solve_two_pointer(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
34+
if head.next is None:
35+
return None
36+
37+
dummy = ListNode(-1)
38+
dummy.next = head
39+
head = dummy
40+
41+
slow = fast = head
42+
while n:
43+
if fast.next:
44+
fast = fast.next
45+
n -= 1
46+
47+
while fast is not None:
48+
if fast.next is None:
49+
slow.next = slow.next.next
50+
break
51+
else:
52+
slow = slow.next
53+
fast = fast.next
54+
55+
return head.next
56+
57+
58+
class _LeetCodeTestCases(TestCase):
59+
def test_1(self):
60+
node_1 = ListNode(1)
61+
node_2 = ListNode(2)
62+
node_3 = ListNode(3)
63+
node_4 = ListNode(4)
64+
node_5 = ListNode(5)
65+
66+
node_1.next = node_2
67+
node_2.next = node_3
68+
node_3.next = node_4
69+
node_4.next = node_5
70+
71+
n = 2
72+
output = [1, 2, 3, 5]
73+
self.assertEqual(Solution().removeNthFromEnd(node_1, n).values(), output)
74+
75+
def test_2(self):
76+
node_1 = ListNode(1)
77+
n = 1
78+
output = []
79+
self.assertEqual(Solution().removeNthFromEnd(node_1, n).values(), output)
80+
81+
def test_3(self):
82+
node_1 = ListNode(1)
83+
node_2 = ListNode(2)
84+
node_1.next = node_2
85+
n = 2
86+
output = [2]
87+
self.assertEqual(Solution().removeNthFromEnd(node_1, n).values(), output)
88+
89+
90+
if __name__ == '__main__':
91+
main()

same-tree/EGON.py

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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 isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
15+
return self.solve_dfs(p, q)
16+
17+
"""
18+
Runtime: 0 ms (Beats 100.00%)
19+
Time Complexity: O(min(p, q))
20+
> dfs를 통해 모든 node를 방문하므로, 각 트리의 node의 갯수를 각각 p, q라 하면, O(min(p, q))
21+
22+
Memory: 16.62 MB (Beats 15.78%)
23+
Space Complexity: O(min(p, q))
24+
> 일반적인 경우 트리의 깊이만큼 dfs 호출 스택이 쌓이나, 최악의 경우 한쪽으로 편향되었다면 O(min(p, q)), upper bound
25+
"""
26+
def solve_dfs(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
27+
28+
def dfs(p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
29+
if p is None and q is None:
30+
return True
31+
elif (p is not None and q is not None) and (p.val == q.val):
32+
return dfs(p.left, q.left) and dfs(p.right, q.right)
33+
else:
34+
return False
35+
36+
return dfs(p, q)
37+
38+
39+
class _LeetCodeTestCases(TestCase):
40+
def test_1(self):
41+
p_1 = TreeNode(1)
42+
p_2 = TreeNode(2)
43+
p_3 = TreeNode(3)
44+
p_1.left = p_2
45+
p_1.right = p_3
46+
47+
q_1 = TreeNode(1)
48+
q_2 = TreeNode(3)
49+
q_3 = TreeNode(3)
50+
q_1.left = q_2
51+
q_1.right = q_3
52+
53+
self.assertEqual(Solution().isSameTree(p_1, q_1), True)
54+
55+
56+
if __name__ == '__main__':
57+
main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from typing import Optional
2+
3+
4+
# Definition for a binary tree node.
5+
class TreeNode:
6+
def __init__(self, val=0, left=None, right=None):
7+
self.val = val
8+
self.left = left
9+
self.right = right
10+
11+
12+
class Codec:
13+
"""
14+
Runtime: 76 ms (Beats 85.72%)
15+
Time Complexity: O(n)
16+
> dfs를 통해 모든 node를 방문하므로, O(n)
17+
18+
Memory: 21.40 MB (Beats 12.82%)
19+
Space Complexity: O(n)
20+
> 일반적인 경우 트리의 깊이만큼 dfs 호출 스택이 쌓이나, 최악의 경우 한쪽으로 편향되었다면 트리의 깊이가 n이 될 수 있으므로 O(n), upper bound
21+
"""
22+
23+
def serialize(self, root):
24+
"""Encodes a tree to a single string.
25+
26+
:type root: TreeNode
27+
:rtype: str
28+
"""
29+
30+
def dfs(node: Optional[TreeNode]) -> None:
31+
if node is None:
32+
result.append("null")
33+
return
34+
result.append(str(node.val))
35+
dfs(node.left)
36+
dfs(node.right)
37+
38+
result = []
39+
dfs(root)
40+
return ','.join(result)
41+
42+
def deserialize(self, data):
43+
"""Decodes your encoded data to tree.
44+
45+
:type data: str
46+
:rtype: TreeNode
47+
"""
48+
49+
def dfs() -> Optional[TreeNode]:
50+
val = next(values)
51+
if val == "null":
52+
return None
53+
node = TreeNode(int(val))
54+
node.left = dfs()
55+
node.right = dfs()
56+
return node
57+
58+
values = iter(data.split(','))
59+
return dfs()
60+
61+
62+
# Your Codec object will be instantiated and called as such:
63+
# ser = Codec()
64+
# deser = Codec()
65+
# ans = deser.deserialize(ser.serialize(root))

0 commit comments

Comments
 (0)