Skip to content

Commit

Permalink
Merge pull request #586 from lymchgmk/week13
Browse files Browse the repository at this point in the history
[EGON] Week13 Solutions
  • Loading branch information
SamTheKorean authored Nov 10, 2024
2 parents 89b1053 + 48e5754 commit c202261
Show file tree
Hide file tree
Showing 5 changed files with 326 additions and 0 deletions.
67 changes: 67 additions & 0 deletions find-median-from-data-stream/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from heapq import heappush, heappop
from unittest import TestCase, main


class MedianFinder:
"""
Runtime: 137 ms (Beats 56.16%)
Time Complexity:
1) addNum
- 최악의 경우 heappush + (heappop + heappush) + (heappop + heapppush) 가 발생할 수 있으므로 O(5 * log n)
> O(5 * log n) ~= O(log n)
2) findMedian
> heap의 루트 값을 가지고 사칙연산만 하므로 O(1)
Memory: 39.94 MB (Beats 5.85%)
Space Complexity: O(n)
- max_heap은 최대 n // 2 개 혹은 n // 2 + 1개의 원소를 가지므로 O(n / 2 + 1), upper bound
- min_heap은 최대 n // 2개의 원소를 가지므로 O(n / 2)
> O(n / 2 + 1) + O(n / 2) ~= O(n) + O(n) ~= O(n)
"""

def __init__(self):
self.max_heap = []
self.min_heap = []

def addNum(self, num):
heappush(self.max_heap, -num)
if self.min_heap and (-self.max_heap[0] > self.min_heap[0]):
heappush(self.min_heap, -heappop(self.max_heap))

if len(self.max_heap) > len(self.min_heap) + 1:
heappush(self.min_heap, -heappop(self.max_heap))
elif len(self.max_heap) < len(self.min_heap):
heappush(self.max_heap, -heappop(self.min_heap))

def findMedian(self):
if self.max_heap and self.min_heap:
if len(self.max_heap) == len(self.min_heap):
return ((-self.max_heap[0]) + self.min_heap[0]) / 2
else:
return -self.max_heap[0]
elif self.min_heap and not self.max_heap:
return self.min_heap[0]
elif not self.min_heap and self.max_heap:
return -self.max_heap[0]
else:
return 0.0


class _LeetCodeTestCases(TestCase):

def test_1(self):
medianFinder = MedianFinder()
medianFinder.addNum(-1)
self.assertEqual(medianFinder.findMedian(), -1.0)
medianFinder.addNum(-2)
self.assertEqual(medianFinder.findMedian(), -1.5)
medianFinder.addNum(-3)
self.assertEqual(medianFinder.findMedian(), -2.0)
medianFinder.addNum(-4)
self.assertEqual(medianFinder.findMedian(), -2.5)
medianFinder.addNum(-5)
self.assertEqual(medianFinder.findMedian(), -3.0)


if __name__ == '__main__':
main()
42 changes: 42 additions & 0 deletions house-robber/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from typing import List
from unittest import TestCase, main


class Solution:
def rob(self, nums: List[int]) -> int:
return self.solve_dp(nums)

"""
Runtime: 0 ms (Beats 100.00%)
Time Complexity: O(n)
- nums 배열을 조회하며 dp 배열을 갱신하므로 O(n)
- 2항에 대한 max 연산을 사용하므로 * O(2)
> O(2 * n) ~= O(n)
Memory: 16.62 MB (Beats 24.05%)
Space Complexity: O(n)
> 길이가 n인 dp 배열을 사용하므로 O(n)
"""

def solve_dp(self, nums: List[int]) -> int:
if len(nums) <= 2:
return max(nums)

dp = [0] * len(nums)
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
for i in range(2, len(nums)):
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])

return dp[-1]


class _LeetCodeTestCases(TestCase):
def test_1(self):
nums = [2,1,1,2]
output = 4
self.assertEqual(Solution().rob(nums), output)


if __name__ == '__main__':
main()
106 changes: 106 additions & 0 deletions lowest-common-ancestor-of-a-binary-search-tree/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
from collections import deque
from unittest import TestCase, main


# Definition of TreeNode:
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None


class Solution:
def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
return self.solve_bfs(root, p, q)

"""
Runtime: 50 ms (Beats 81.68%)
Time Complexity:`
- bfs를 이용해 ancetor와 nodes를 초기화 하는데 트리의 모든 p와 q를 포함할 때까지 모든 node를 조회하는데, O(n)
- p로 부터 부모를 따라 올라가는데, 트리의 모든 node가 편향적으로 연결된 경우 최대 O(n), upper bound
- q로 부터 부모를 따라 올라가는데, 단, 위 p의 추적 path와 겹치지 않는 곳만 올라가므로, 총합이 O(n)
> O(n) + (O(P) + O(Q)) = O(n) + O(n) ~= O(n)
Memory: 21.20 MB (Beats 14.40%)
Space Complexity: O(n)
- p, q가 트리의 리프노드인 경우 dq의 크기는 O(n), upper bound
- p_ancestors와 q_anscestor의 크기는 합쳐서 O(n)
> O(n) + O(n) ~= O(n)
"""
def solve_bfs(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
dq = deque([root])
ancestor = {root.val: root.val}
nodes = {root.val: root}
while dq:
if p.val in ancestor and q.val in ancestor:
break

curr_node = dq.popleft()
if curr_node.left:
ancestor[curr_node.left.val] = curr_node.val
dq.append(curr_node.left)
nodes[curr_node.left.val] = curr_node.left
if curr_node.right:
ancestor[curr_node.right.val] = curr_node.val
dq.append(curr_node.right)
nodes[curr_node.right.val] = curr_node.right

p_val = p.val
p_ancestors = set()
p_ancestors.add(root.val)
while p_val in ancestor and p_val != root.val:
p_ancestors.add(p_val)
p_ancestors.add(ancestor[p_val])
p_val = ancestor[p_val]

q_val = q.val
q_ancestors = set()
q_ancestors.add(q_val)
while q_val in ancestor and q_val not in p_ancestors:
q_ancestors.add(q_val)
q_ancestors.add(ancestor[q_val])
q_val = ancestor[q_val]

common_ancestor = p_ancestors & q_ancestors
if common_ancestor:
return nodes[common_ancestor.pop()]
else:
return root


class _LeetCodeTestCases(TestCase):

def test_1(self):
root = TreeNode(5)
node1 = TreeNode(3)
node2 = TreeNode(6)
node3 = TreeNode(2)
node4 = TreeNode(4)
node5 = TreeNode(1)

root.left = node1
root.right = node2
node1.left = node3
node1.right = node4
node3.left = node5

p = node5
q = node1
output = root
self.assertEqual(Solution().lowestCommonAncestor(root, p, q), output)

def test_2(self):
root = TreeNode(2)
node1 = TreeNode(1)

root.left = node1

p = TreeNode(2)
q = TreeNode(1)
output = root
self.assertEqual(Solution().lowestCommonAncestor(root, p, q), output)


if __name__ == '__main__':
main()
66 changes: 66 additions & 0 deletions meeting-rooms/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from typing import List
from unittest import TestCase, main


# Definition of Interval:
class Interval(object):
def __init__(self, start, end):
self.start = start
self.end = end


class Solution:
def canAttendMeetings(self, intervals: List[Interval]) -> bool:
return self.solve_sort_stack(intervals)

"""
LintCode 로그인이 안되어서 hhttps://neetcode.io/problems/meeting-schedule 에서 실행시키고 통과만 확인했습니다.
Runtime: ? ms (Beats ?%)
Time Complexity: O(n log n)
- intervals 정렬에 O(n log n)
- intervals 조회하며 stack의 마지막 값의 end와 현재 interval의 start를 비교하는데 O(n)
> O(n log n) + O(n) ~= O(n log n)
Memory: ? MB (Beats ?%)
Space Complexity: O(n)
- intervals 메모리를 그대로 사용하면서 정렬했으므로 O(1)
- stack의 크기는 최대 intervals와 같아질 수 있으므로 O(n)
"""

def solve_sort_stack(self, intervals: List[Interval]) -> bool:
intervals.sort(key=lambda x: x.start)

stack: List[Interval] = []
for interval in intervals:
if not stack:
stack.append(interval)
continue

if stack[-1].end > interval.start:
return False
else:
stack.append(interval)

return True


class _LeetCodeTestCases(TestCase):
def test_1(self):
intervals = [Interval(0,30), Interval(5,10), Interval(15,20)]
output = False
self.assertEqual(Solution().canAttendMeetings(intervals), output)

def test_2(self):
intervals = [Interval(5, 8), Interval(9,15)]
output = False
self.assertEqual(Solution().canAttendMeetings(intervals), output)

def test_3(self):
intervals = [Interval(0, 1), Interval(1, 2)]
output = False
self.assertEqual(Solution().canAttendMeetings(intervals), output)


if __name__ == '__main__':
main()
45 changes: 45 additions & 0 deletions non-overlapping-intervals/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from typing import List
from unittest import TestCase, main


class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
return self.solve_stack(intervals)

"""
LintCode 로그인이 안되어서 hhttps://neetcode.io/problems/meeting-schedule 에서 실행시키고 통과만 확인했습니다.
Runtime: 66 ms (Beats 85.10%)
Time Complexity: O(n log n)
- intervals를 정렬하는데 O(n log n)
- intervals를 조회하며 result stack을 갱신하는데 O(n)
- 2항에 대한 or 연산 및 append 메서드만 쓰므로 * O(1)
> O(n log n) + O(n) ~= O(n log n)
Memory: 50.98 MB (Beats 74.30%)
Space Complexity: O(n)
- intervals 정렬도 기존 intervals를 사용하므로 O(1)
- result의 크기가 최대 interval과 같아질 수 있으므로 O(n)
> O(1) + O(n) ~= O(n)
"""
def solve_stack(self, intervals: List[List[int]]) -> int:
if not intervals:
return 0

intervals.sort(key=lambda x: x[1])
result = []
for interval in intervals:
if not result or interval[0] >= result[-1][1]:
result.append(interval)

return len(intervals) - len(result)


class _LeetCodeTestCases(TestCase):
def test_1(self):
intervals = [[1,2],[2,3],[3,4],[1,3]]
output = 1
self.assertEqual(Solution().eraseOverlapIntervals(intervals), output)


if __name__ == '__main__':
main()

0 comments on commit c202261

Please sign in to comment.