Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EGON] Week10 Solutions #542

Merged
merged 7 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions course-schedule/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from collections import deque
from typing import List
from unittest import TestCase, main


class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
return self.solve_topological_sort(numCourses, prerequisites)

"""
Runtime: 1 ms (Beats 100.00%)
Time Complexity: o(c + p)
- graph 및 rank 갱신에 prerequisites의 길이 p만큼 조회하는데 O(p)
- queue의 초기 노드 삽입에 numCourses만큼 조회하는데 O(c)
- queue에서 위상 정렬로 탐색하는데 모든 노드와 간선을 조회하는데 O(c + p)
> O(p) + O(c) + O(c + p) ~= o(c + p)

Memory: 17.85 MB (Beats 99.94%)
Space Complexity: O(c)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

28-30 라인에서 그래프를 저장할 때, c 뿐만 아니라 p도 공간복잡도에 영향을 끼치지 않을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감사합니다 dict에서 value에 대해 sc를 생각 못했네요; key의 갯수는 c에 비례하고, value의 모든 원소의 갯수는 p에 비례하니 O(c + p)가 맞을 것 같습니다.

- graph 변수 사용에 O(c + p)
- rank 변수 사용에 O(c)
- queue 변수 사용에서 최대 크기는 graph의 크기와 같으므로 O(c)
> O(c + p) + O(c) + O(c) ~= O(c + p)
"""
def solve_topological_sort(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
graph = {i: [] for i in range(numCourses)}
rank = [0] * numCourses
for u, v in prerequisites:
graph[v].append(u)
rank[u] += 1

queue = deque()
for i in range(numCourses):
if rank[i] == 0:
queue.append(i)

count = 0
while queue:
node = queue.popleft()
count += 1
for neighbor in graph[node]:
rank[neighbor] -= 1
if rank[neighbor] == 0:
queue.append(neighbor)

return count == numCourses


class _LeetCodeTestCases(TestCase):
def test_1(self):
numCourses = 5
prerequisites = [[1,4],[2,4],[3,1],[3,2]]
output = True
self.assertEqual(Solution.canFinish(Solution(), numCourses, prerequisites), output)


if __name__ == '__main__':
main()
48 changes: 48 additions & 0 deletions invert-binary-tree/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from typing import Optional
from unittest import TestCase, main


# Definition for a binary tree node.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right


class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
return self.solve_dfs(root)

"""
Runtime: 0 ms (Beats 100.00%)
Time Complexity: O(n)
> 트리의 모든 node를 방문하므로 O(n)

Memory: 16.53 MB (Beats 25.95%)
Space Complexity: O(n)
> stack의 최대 크기는 트리의 최장 경로를 이루는 node의 갯수이고, 최악의 경우 트리의 한 쪽으로 모든 node가 이어져있는 경우이므로 O(n), upper bound
"""
def solve_dfs(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root is None:
return root

stack = [root]
while stack:
curr_node = stack.pop()
curr_node.left, curr_node.right = curr_node.right, curr_node.left
if curr_node.left:
stack.append(curr_node.left)
if curr_node.right:
stack.append(curr_node.right)

return root


class _LeetCodeTestCases(TestCase):
def test_1(self):
return


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


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

"""
Runtime: 3130 ms (Beats 11.42%)
Time Complexity: O(n * m)
- dp 배열 생성에 nums의 길이 n 만큼 조회하는데 O(n)
- 생성한 dp 배열을 조회하는데 O(n)
- dp[i]에서 점프하는 범위에 의해 * O(2 * m)
> O(n) + O(n) * O(2 * m) ~= O(n * m)

Memory: 17.80 MB (Beats 72.54%)
Space Complexity: O(n)
> nums의 길이에 비례하는 dp 배열 하나만 사용, O(n)
"""
def solve_dp(self, nums: List[int]) -> bool:
dp = [True if i == 0 else False for i in range(len(nums))]
for i in range(len(nums)):
if dp[-1] is True:
return True

if dp[i] is True:
for jump in range(nums[i] + 1):
if 0 <= i + jump < len(dp):
dp[i + jump] = True

return dp[-1]


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


if __name__ == '__main__':
main()
55 changes: 55 additions & 0 deletions merge-k-sorted-lists/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from heapq import heappush, heappop
from typing import List, Optional
from unittest import TestCase, main


class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next


class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
return self.solve_priority_queue(lists)

"""
Runtime: 7 ms (Beats 100.00%)
Time Complexity: O(n * m)
- lists의 길이 k만큼 조회에 O(k)
- 힙의 크기가 최대 k이므로, heappush에 * O(log k)
- heap의 크기는 최대 k이므로,
- heappop하는데 O(k * log k)
- heappush하는데 lists를 이루는 list를 이루는 모든 원소들의 총 갯수를 n이라 하면, O(n * log k)
> O(k * log k) + O(k * log k) + O(n * log k) ~= O(max(k, n) * log k) = O(n * log k)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

22번 라인은 아마 O(logk)인데, 21라인의 k도 포함하신것으로 보이네요. 제가 이해한것이 맞을까요?
24라인의 경우 O(k * log k) + O(k * log k) + O(n * log k)로 산출해주셨는데, 이를 O(max(k, n) * log k)로 단순화하기에는 문제 조건에 k, n에 대한 조건이 명시되어있지 않은것으로 보입니다.
결과적으로 아마도 O((k + n) * log k)로 표현하는것이 적절하지 않을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

k 포함이 맞습니다. k는 lists의 길이이고 n은 lists의 원소인 list의 원소 모두의 갯수로 최종적으로 병합된 list의 node의 갯수와 같다면, n = a * k이고 a는 lists의 원소인 list의 평균길이라 생각해서 저렇게 합쳤는데, 지금 보니 역으로도 설명 가능할 것 같습니다.
max(k, n) = max(k, a * k) = a * k = n ~= k

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아, n이 이미 a라는 변수를 통해 내부적으로 k를 포함하고 있다고 볼 수 있었네요. 설명해주셔서 감사합니다!


Memory: 19.44 MB (Beats 58.42%)
Space Complexity: O(k)
> heap의 크기는 lists의 길이 k에 비례하므로, O(k)
"""
def solve_priority_queue(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
root = result = ListNode(None)
heap = []

for i in range(len(lists)):
if lists[i]:
heappush(heap, (lists[i].val, i, lists[i]))

while heap:
node = heappop(heap)
_, idx, result.next = node

result = result.next
if result.next:
heappush(heap, (result.next.val, idx, result.next))

return root.next


class _LeetCodeTestCases(TestCase):
def test_1(self):
self.assertEqual(True, True)


if __name__ == '__main__':
main()
67 changes: 67 additions & 0 deletions search-in-rotated-sorted-array/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from typing import List
from unittest import TestCase, main


class Solution:
def search(self, nums: List[int], target: int) -> int:
return self.solve_binary_search(nums, target)

"""
Runtime: 4 ms (Beats 100.00%)
Time Complexity: O(log n)
> nums를 이진탐색으로 조회하므로 O(log n)

Memory: 17.03 MB (Beats 10.00%)
Space Complexity: O(1)
> index를 위한 정수형 변수만 사용하므로 O(1)
"""
def solve_binary_search(self, nums: List[int], target: int) -> int:
lo, hi = 0, len(nums) - 1
while lo < hi:
mid = (lo + hi) // 2
if nums[mid] == target:
return mid

if nums[lo] <= nums[mid]:
if nums[lo] <= target <= nums[mid]:
hi = mid
else:
lo = mid + 1

else:
if nums[mid] <= target <= nums[hi]:
lo = mid + 1
else:
hi = mid

return lo if nums[lo] == target else -1


class _LeetCodeTestCases(TestCase):
def test_1(self):
nums = [4,5,6,7,0,1,2]
target = 0
output = 4
self.assertEqual(Solution.search(Solution(), nums, target), output)

def test_2(self):
nums = [4,5,6,7,0,1,2]
target = 3
output = -1
self.assertEqual(Solution.search(Solution(), nums, target), output)

def test_3(self):
nums = [1]
target = 0
output = -1
self.assertEqual(Solution.search(Solution(), nums, target), output)

def test_4(self):
nums = [3, 1]
target = 1
output = 1
self.assertEqual(Solution.search(Solution(), nums, target), output)


if __name__ == '__main__':
main()