Skip to content

Commit c202261

Browse files
authored
Merge pull request #586 from lymchgmk/week13
[EGON] Week13 Solutions
2 parents 89b1053 + 48e5754 commit c202261

File tree

5 files changed

+326
-0
lines changed

5 files changed

+326
-0
lines changed

find-median-from-data-stream/EGON.py

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
from heapq import heappush, heappop
2+
from unittest import TestCase, main
3+
4+
5+
class MedianFinder:
6+
"""
7+
Runtime: 137 ms (Beats 56.16%)
8+
Time Complexity:
9+
1) addNum
10+
- 최악의 경우 heappush + (heappop + heappush) + (heappop + heapppush) 가 발생할 수 있으므로 O(5 * log n)
11+
> O(5 * log n) ~= O(log n)
12+
2) findMedian
13+
> heap의 루트 값을 가지고 사칙연산만 하므로 O(1)
14+
15+
Memory: 39.94 MB (Beats 5.85%)
16+
Space Complexity: O(n)
17+
- max_heap은 최대 n // 2 개 혹은 n // 2 + 1개의 원소를 가지므로 O(n / 2 + 1), upper bound
18+
- min_heap은 최대 n // 2개의 원소를 가지므로 O(n / 2)
19+
> O(n / 2 + 1) + O(n / 2) ~= O(n) + O(n) ~= O(n)
20+
"""
21+
22+
def __init__(self):
23+
self.max_heap = []
24+
self.min_heap = []
25+
26+
def addNum(self, num):
27+
heappush(self.max_heap, -num)
28+
if self.min_heap and (-self.max_heap[0] > self.min_heap[0]):
29+
heappush(self.min_heap, -heappop(self.max_heap))
30+
31+
if len(self.max_heap) > len(self.min_heap) + 1:
32+
heappush(self.min_heap, -heappop(self.max_heap))
33+
elif len(self.max_heap) < len(self.min_heap):
34+
heappush(self.max_heap, -heappop(self.min_heap))
35+
36+
def findMedian(self):
37+
if self.max_heap and self.min_heap:
38+
if len(self.max_heap) == len(self.min_heap):
39+
return ((-self.max_heap[0]) + self.min_heap[0]) / 2
40+
else:
41+
return -self.max_heap[0]
42+
elif self.min_heap and not self.max_heap:
43+
return self.min_heap[0]
44+
elif not self.min_heap and self.max_heap:
45+
return -self.max_heap[0]
46+
else:
47+
return 0.0
48+
49+
50+
class _LeetCodeTestCases(TestCase):
51+
52+
def test_1(self):
53+
medianFinder = MedianFinder()
54+
medianFinder.addNum(-1)
55+
self.assertEqual(medianFinder.findMedian(), -1.0)
56+
medianFinder.addNum(-2)
57+
self.assertEqual(medianFinder.findMedian(), -1.5)
58+
medianFinder.addNum(-3)
59+
self.assertEqual(medianFinder.findMedian(), -2.0)
60+
medianFinder.addNum(-4)
61+
self.assertEqual(medianFinder.findMedian(), -2.5)
62+
medianFinder.addNum(-5)
63+
self.assertEqual(medianFinder.findMedian(), -3.0)
64+
65+
66+
if __name__ == '__main__':
67+
main()

house-robber/EGON.py

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from typing import List
2+
from unittest import TestCase, main
3+
4+
5+
class Solution:
6+
def rob(self, nums: List[int]) -> int:
7+
return self.solve_dp(nums)
8+
9+
"""
10+
Runtime: 0 ms (Beats 100.00%)
11+
Time Complexity: O(n)
12+
- nums 배열을 조회하며 dp 배열을 갱신하므로 O(n)
13+
- 2항에 대한 max 연산을 사용하므로 * O(2)
14+
> O(2 * n) ~= O(n)
15+
16+
Memory: 16.62 MB (Beats 24.05%)
17+
Space Complexity: O(n)
18+
> 길이가 n인 dp 배열을 사용하므로 O(n)
19+
"""
20+
21+
def solve_dp(self, nums: List[int]) -> int:
22+
if len(nums) <= 2:
23+
return max(nums)
24+
25+
dp = [0] * len(nums)
26+
dp[0] = nums[0]
27+
dp[1] = max(nums[0], nums[1])
28+
for i in range(2, len(nums)):
29+
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])
30+
31+
return dp[-1]
32+
33+
34+
class _LeetCodeTestCases(TestCase):
35+
def test_1(self):
36+
nums = [2,1,1,2]
37+
output = 4
38+
self.assertEqual(Solution().rob(nums), output)
39+
40+
41+
if __name__ == '__main__':
42+
main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
from collections import deque
2+
from unittest import TestCase, main
3+
4+
5+
# Definition of TreeNode:
6+
class TreeNode:
7+
def __init__(self, x):
8+
self.val = x
9+
self.left = None
10+
self.right = None
11+
12+
13+
class Solution:
14+
def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
15+
return self.solve_bfs(root, p, q)
16+
17+
"""
18+
Runtime: 50 ms (Beats 81.68%)
19+
Time Complexity:`
20+
- bfs를 이용해 ancetor와 nodes를 초기화 하는데 트리의 모든 p와 q를 포함할 때까지 모든 node를 조회하는데, O(n)
21+
- p로 부터 부모를 따라 올라가는데, 트리의 모든 node가 편향적으로 연결된 경우 최대 O(n), upper bound
22+
- q로 부터 부모를 따라 올라가는데, 단, 위 p의 추적 path와 겹치지 않는 곳만 올라가므로, 총합이 O(n)
23+
> O(n) + (O(P) + O(Q)) = O(n) + O(n) ~= O(n)
24+
25+
Memory: 21.20 MB (Beats 14.40%)
26+
Space Complexity: O(n)
27+
- p, q가 트리의 리프노드인 경우 dq의 크기는 O(n), upper bound
28+
- p_ancestors와 q_anscestor의 크기는 합쳐서 O(n)
29+
> O(n) + O(n) ~= O(n)
30+
"""
31+
def solve_bfs(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
32+
dq = deque([root])
33+
ancestor = {root.val: root.val}
34+
nodes = {root.val: root}
35+
while dq:
36+
if p.val in ancestor and q.val in ancestor:
37+
break
38+
39+
curr_node = dq.popleft()
40+
if curr_node.left:
41+
ancestor[curr_node.left.val] = curr_node.val
42+
dq.append(curr_node.left)
43+
nodes[curr_node.left.val] = curr_node.left
44+
if curr_node.right:
45+
ancestor[curr_node.right.val] = curr_node.val
46+
dq.append(curr_node.right)
47+
nodes[curr_node.right.val] = curr_node.right
48+
49+
p_val = p.val
50+
p_ancestors = set()
51+
p_ancestors.add(root.val)
52+
while p_val in ancestor and p_val != root.val:
53+
p_ancestors.add(p_val)
54+
p_ancestors.add(ancestor[p_val])
55+
p_val = ancestor[p_val]
56+
57+
q_val = q.val
58+
q_ancestors = set()
59+
q_ancestors.add(q_val)
60+
while q_val in ancestor and q_val not in p_ancestors:
61+
q_ancestors.add(q_val)
62+
q_ancestors.add(ancestor[q_val])
63+
q_val = ancestor[q_val]
64+
65+
common_ancestor = p_ancestors & q_ancestors
66+
if common_ancestor:
67+
return nodes[common_ancestor.pop()]
68+
else:
69+
return root
70+
71+
72+
class _LeetCodeTestCases(TestCase):
73+
74+
def test_1(self):
75+
root = TreeNode(5)
76+
node1 = TreeNode(3)
77+
node2 = TreeNode(6)
78+
node3 = TreeNode(2)
79+
node4 = TreeNode(4)
80+
node5 = TreeNode(1)
81+
82+
root.left = node1
83+
root.right = node2
84+
node1.left = node3
85+
node1.right = node4
86+
node3.left = node5
87+
88+
p = node5
89+
q = node1
90+
output = root
91+
self.assertEqual(Solution().lowestCommonAncestor(root, p, q), output)
92+
93+
def test_2(self):
94+
root = TreeNode(2)
95+
node1 = TreeNode(1)
96+
97+
root.left = node1
98+
99+
p = TreeNode(2)
100+
q = TreeNode(1)
101+
output = root
102+
self.assertEqual(Solution().lowestCommonAncestor(root, p, q), output)
103+
104+
105+
if __name__ == '__main__':
106+
main()

meeting-rooms/EGON.py

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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 canAttendMeetings(self, intervals: List[Interval]) -> bool:
14+
return self.solve_sort_stack(intervals)
15+
16+
"""
17+
LintCode 로그인이 안되어서 hhttps://neetcode.io/problems/meeting-schedule 에서 실행시키고 통과만 확인했습니다.
18+
19+
Runtime: ? ms (Beats ?%)
20+
Time Complexity: O(n log n)
21+
- intervals 정렬에 O(n log n)
22+
- intervals 조회하며 stack의 마지막 값의 end와 현재 interval의 start를 비교하는데 O(n)
23+
> O(n log n) + O(n) ~= O(n log n)
24+
25+
Memory: ? MB (Beats ?%)
26+
Space Complexity: O(n)
27+
- intervals 메모리를 그대로 사용하면서 정렬했으므로 O(1)
28+
- stack의 크기는 최대 intervals와 같아질 수 있으므로 O(n)
29+
"""
30+
31+
def solve_sort_stack(self, intervals: List[Interval]) -> bool:
32+
intervals.sort(key=lambda x: x.start)
33+
34+
stack: List[Interval] = []
35+
for interval in intervals:
36+
if not stack:
37+
stack.append(interval)
38+
continue
39+
40+
if stack[-1].end > interval.start:
41+
return False
42+
else:
43+
stack.append(interval)
44+
45+
return True
46+
47+
48+
class _LeetCodeTestCases(TestCase):
49+
def test_1(self):
50+
intervals = [Interval(0,30), Interval(5,10), Interval(15,20)]
51+
output = False
52+
self.assertEqual(Solution().canAttendMeetings(intervals), output)
53+
54+
def test_2(self):
55+
intervals = [Interval(5, 8), Interval(9,15)]
56+
output = False
57+
self.assertEqual(Solution().canAttendMeetings(intervals), output)
58+
59+
def test_3(self):
60+
intervals = [Interval(0, 1), Interval(1, 2)]
61+
output = False
62+
self.assertEqual(Solution().canAttendMeetings(intervals), output)
63+
64+
65+
if __name__ == '__main__':
66+
main()

non-overlapping-intervals/EGON.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from typing import List
2+
from unittest import TestCase, main
3+
4+
5+
class Solution:
6+
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
7+
return self.solve_stack(intervals)
8+
9+
"""
10+
LintCode 로그인이 안되어서 hhttps://neetcode.io/problems/meeting-schedule 에서 실행시키고 통과만 확인했습니다.
11+
12+
Runtime: 66 ms (Beats 85.10%)
13+
Time Complexity: O(n log n)
14+
- intervals를 정렬하는데 O(n log n)
15+
- intervals를 조회하며 result stack을 갱신하는데 O(n)
16+
- 2항에 대한 or 연산 및 append 메서드만 쓰므로 * O(1)
17+
> O(n log n) + O(n) ~= O(n log n)
18+
Memory: 50.98 MB (Beats 74.30%)
19+
Space Complexity: O(n)
20+
- intervals 정렬도 기존 intervals를 사용하므로 O(1)
21+
- result의 크기가 최대 interval과 같아질 수 있으므로 O(n)
22+
> O(1) + O(n) ~= O(n)
23+
"""
24+
def solve_stack(self, intervals: List[List[int]]) -> int:
25+
if not intervals:
26+
return 0
27+
28+
intervals.sort(key=lambda x: x[1])
29+
result = []
30+
for interval in intervals:
31+
if not result or interval[0] >= result[-1][1]:
32+
result.append(interval)
33+
34+
return len(intervals) - len(result)
35+
36+
37+
class _LeetCodeTestCases(TestCase):
38+
def test_1(self):
39+
intervals = [[1,2],[2,3],[3,4],[1,3]]
40+
output = 1
41+
self.assertEqual(Solution().eraseOverlapIntervals(intervals), output)
42+
43+
44+
if __name__ == '__main__':
45+
main()

0 commit comments

Comments
 (0)