-
Notifications
You must be signed in to change notification settings - Fork 126
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #586 from lymchgmk/week13
[EGON] Week13 Solutions
- Loading branch information
Showing
5 changed files
with
326 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |