diff --git a/course-schedule/alphaorderly.py b/course-schedule/alphaorderly.py new file mode 100644 index 0000000000..20fbba006f --- /dev/null +++ b/course-schedule/alphaorderly.py @@ -0,0 +1,39 @@ + +""" +시간복잡도: O(n + m) +공간복잡도: O(n + m) + +- 각 과목의 진입 차수(in_degree)를 계산한다. +- 각 과목의 인접 리스트(graph)를 구성한다. +- 진입 차수가 0인 과목을 큐에 추가한다. +- 큐에서 과목을 하나씩 꺼내고, 그 과목을 선수과목으로 가지는 모든 과목의 진입 차수를 1씩 감소시킨다. +- 진입 차수가 0이 된 과목을 큐에 추가한다. +- 큐를 모두 처리한 후, 방문한 과목의 수가 전체 과목 수와 같은지 확인한다. +""" +class Solution: + def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: + in_degree = [0] * numCourses + graph = defaultdict(list) + entered_courses = 0 + + for s, e in prerequisites: + in_degree[s] += 1 + graph[e].append(s) + + queue = deque([]) + + for course, count in enumerate(in_degree): + if count == 0: + queue.append(course) + entered_courses += 1 + + while queue: + course = queue.popleft() + + for next_course in graph[course]: + in_degree[next_course] -= 1 + if in_degree[next_course] == 0: + entered_courses += 1 + queue.append(next_course) + + return entered_courses == numCourses diff --git a/invert-binary-tree/alphaorderly.py b/invert-binary-tree/alphaorderly.py new file mode 100644 index 0000000000..d8269c4571 --- /dev/null +++ b/invert-binary-tree/alphaorderly.py @@ -0,0 +1,23 @@ +# 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 + +""" +시간복잡도: O(n) +공간복잡도: O(h) - 트리의 높이(h)만큼 재귀 호출이 쌓인다. + +- 루트 노드부터 시작해, 왼쪽과 오른쪽 자식을 각각 재귀적으로 반전한다. +- 재귀 함수는 현재 노드가 None이면 바로 None을 반환한다. +- 왼쪽과 오른쪽 자식을 반전한 결과를 각각 root.right, root.left로 할당하여 두 자식을 서로 바꾼다. +- 최종적으로 반전된 루트 노드를 반환한다. +""" +class Solution: + def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + if not root: + return None + + root.left, root.right = self.invertTree(root.right), self.invertTree(root.left) + return root diff --git a/jump-game/alphaorderly.py b/jump-game/alphaorderly.py new file mode 100644 index 0000000000..1e7abcc7c1 --- /dev/null +++ b/jump-game/alphaorderly.py @@ -0,0 +1,22 @@ +""" +시간복잡도: O(n) +공간복잡도: O(1) + +- 현재 위치(index)에서 갈 수 있는 가장 먼 위치(furthest)를 갱신한다. +- furthest가 현재 위치보다 작으면 도달할 수 없으므로 False를 반환한다. +- 갱신된 furthest가 마지막 인덱스 이상이 되면 True를 반환한다. +""" +class Solution: + def canJump(self, nums: List[int]) -> bool: + N = len(nums) + furthest = 0 + + for index, jump in enumerate(nums): + if furthest < index: + return False + + furthest = max(furthest, index + jump) + if furthest >= N - 1: + return True + + return False diff --git a/merge-k-sorted-lists/alphaorderly.py b/merge-k-sorted-lists/alphaorderly.py new file mode 100644 index 0000000000..b75df30f59 --- /dev/null +++ b/merge-k-sorted-lists/alphaorderly.py @@ -0,0 +1,35 @@ +""" +시간복잡도: O(n log k) +공간복잡도: O(k) +- k는 연결 리스트의 개수, n은 모든 노드의 개수 + +- 각 연결 리스트의 첫 번째 노드 값을 (값, 인덱스) 형태로 힙에 추가한다. +- 힙에서 가장 작은 값을 꺼내 결과 더미 연결 리스트에 노드로 연결한다. +- 꺼낸 노드가 속했던 리스트의 다음 노드가 있으면 힙에 추가한다. +- 힙이 빌 때까지 위 과정을 반복한다. +- 더미 연결 리스트의 다음 노드를 반환한다. +""" +# Definition for singly-linked list. +# 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]: + ans = ListNode() + head = ans + + values = [(node.val, index) for index, node in enumerate(lists) if node] + heapify(values) + + while values: + _, index = heappop(values) + + head.next = lists[index] + head = head.next + + lists[index] = lists[index].next + if lists[index]: + heappush(values, (lists[index].val, index)) + + return ans.next diff --git a/search-in-rotated-sorted-array/alphaorderly.py b/search-in-rotated-sorted-array/alphaorderly.py new file mode 100644 index 0000000000..4bdeb20276 --- /dev/null +++ b/search-in-rotated-sorted-array/alphaorderly.py @@ -0,0 +1,38 @@ +""" +시간복잡도: O(log n) +공간복잡도: O(1) + +- 회전된 정렬 배열에서 이진 탐색으로 target을 찾는다. +- mid와 target이 원래 배열의 왼쪽 절반에 있었는지, 오른쪽 절반에 있었는지를 구분한다. + - 기준: nums[mid] > nums[N-1] 이면 mid가 왼쪽(회전되기 전 더 작은 쪽), 아니면 오른쪽. + - target도 같은 기준으로 왼쪽/오른쪽인지 판별. +- mid와 target이 서로 다른 쪽에 있으면, target이 있는 쪽으로 포인터를 이동 (left 또는 right 값을 조정). +- mid == target 이면 mid 반환. +- 찾지 못하면 -1 반환. +""" +class Solution: + def search(self, nums: List[int], target: int) -> int: + N = len(nums) + left = 0 + right = N - 1 + + while left <= right: + mid = (left + right) // 2 + + if target == nums[mid]: + return mid + + mid_left = nums[mid] > nums[N - 1] + target_left = target > nums[N - 1] + + if nums[mid] > target: + if mid_left != target_left: + left = mid + 1 + else: + right = mid - 1 + elif mid_left != target_left: + right = mid - 1 + else: + left = mid + 1 + + return -1