-
Notifications
You must be signed in to change notification settings - Fork 126
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
Changes from all commits
3986165
448a036
1b6b41d
0a669d5
50af881
ac155d9
e9629f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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) | ||
- 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() |
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() |
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() |
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 22번 라인은 아마 O(logk)인데, 21라인의 k도 포함하신것으로 보이네요. 제가 이해한것이 맞을까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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의 평균길이라 생각해서 저렇게 합쳤는데, 지금 보니 역으로도 설명 가능할 것 같습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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() |
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() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
28-30 라인에서 그래프를 저장할 때, c 뿐만 아니라 p도 공간복잡도에 영향을 끼치지 않을까요?
There was a problem hiding this comment.
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)가 맞을 것 같습니다.