diff --git a/course-schedule/hi-rachel.py b/course-schedule/hi-rachel.py new file mode 100644 index 000000000..f4eb7e583 --- /dev/null +++ b/course-schedule/hi-rachel.py @@ -0,0 +1,58 @@ +""" +https://leetcode.com/problems/course-schedule/ + +문제: 수강 해야 하는 모든 강좌의 수 numCourses가 주어질 때, 모든 강좌를 끝낼 수 있으면 true, 아니면 false를 반환해라. + prerequisites[i] = [ai, bi], bi를 수강하기 위해선 반드시 ai를 사전 수강해야만 한다. + +풀이: 사이클이 있으면 수강 불가능 (순환 참조), 없으면 수강 가능 + 각 강좌를 Node로 보고, 선행 과목 관계를 방향이 있는 간선(Edge)로 보면 -> 방향 그래프 + + BFS 풀이 + 1. 그래프 만들기 + graph[a] = [b, c] 이면 a를 듣기 전에 b, c를 들어야 한다. + graph[b] = [a] 이면 b를 듣기 전에 a를 들어야 한다. + + 2. 진입 차수 계산 + 진입 차수: 어떤 노드로 들어오는 간선의 수 + 진입 차수가 0인 노드는 바로 들을 수 있는 강의 + + 3. Queue에 진입 차수가 0인 노드부터 넣고 시작 + Queue에서 꺼낸 노드를 기준으로, 연결된 노드들의 진입차수를 하나씩 줄인다. + 진입차수가 0이 된 노드는 새로 Queue에 추가 + + 4, 처리된 노드 수가 전체 강의 수와 같으면 True, 아니면 False + +TC: O(V + E), V: 과목 수, E: prerequisite 관계 수 +SC: O(V + E), 그래프 + 진입차수 배열 +""" + +from typing import List +from collections import defaultdict, deque + +class Solution: + def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: + graph = defaultdict(list) + indegree = [0] * numCourses + + # 1. 그래프 만들기, 2. 진입차수 계산 + # 수강 강의, 사전 강의 + for course, prereq in prerequisites: + graph[prereq].append(course) + indegree[course] += 1 + + # 3. Queue에 진입 차수가 0인 노드부터 넣고 시작 + queue = deque([i for i in range(numCourses) if indegree[i] == 0]) + completed = 0 + + # 4. BFS 탐색, 처리된 노드 수가 전체 강의 수와 같으면 True, 아니면 False + while queue: + # queue에서 꺼낸 과목을 수강 완료 처리 + current = queue.popleft() + completed += 1 + + for neighbor in graph[current]: + indegree[neighbor] -= 1 + if indegree[neighbor] == 0: + queue.append(neighbor) + + return completed == numCourses diff --git a/invert-binary-tree/hi-rachel.py b/invert-binary-tree/hi-rachel.py new file mode 100644 index 000000000..cdeee27f1 --- /dev/null +++ b/invert-binary-tree/hi-rachel.py @@ -0,0 +1,37 @@ +from typing import Optional + +# 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 + +""" +재귀 풀이 + +TC: O(n), SC: O(n) +n = 트리 내의 노드 수 +""" +class Solution: + def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + if not root: + return + root.left, root.right = self.invertTree(root.right), self.invertTree(root.left) + return root + +""" +스택 풀이 + +TC: O(n), SC: O(n) +""" +class Solution: + def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + stack = [root] + while stack: + node = stack.pop() + if not node: + continue + node.left, node.right = node.right, node.left + stack += [node.left, node.right] + return root diff --git a/jump-game/hi-rachel.py b/jump-game/hi-rachel.py new file mode 100644 index 000000000..e97cd2905 --- /dev/null +++ b/jump-game/hi-rachel.py @@ -0,0 +1,22 @@ +""" +https://leetcode.com/problems/jump-game/description/ + +문제: 배열 nums가 주어질 때, 각 인덱스에서 최대 점프 거리를 나타내는 배열이다. + 배열의 마지막 인덱스에 도달할 수 있으면 true, 아니면 false를 반환해라. + +풀이: + 1. 현재 인덱스에서 최대 점프 거리를 계산한다. + 2. 최대 점프 거리가 배열의 마지막 인덱스를 넘으면 true, 아니면 false를 반환한다. + +TC: O(n), SC: O(1) +""" + +from typing import List + +class Solution: + def canJump(self, nums: List[int]) -> bool: + reach = 0 # 현재까지 도달 가능한 최대 인덱스 + for idx in range(len(nums)): # 각 위치 순회 + if idx <= reach: # 현재 인덱스가 도달 가능한 최대 인덱스 이하면 + reach = max(reach, idx + nums[idx]) # 최대 점프 거리 갱신 + return len(nums) - 1 <= reach # 마지막 인덱스가 도달 가능한 최대 인덱스 이상이면 true diff --git a/merge-k-sorted-lists/hi-rachel.py b/merge-k-sorted-lists/hi-rachel.py new file mode 100644 index 000000000..38b48359e --- /dev/null +++ b/merge-k-sorted-lists/hi-rachel.py @@ -0,0 +1,57 @@ +""" +https://leetcode.com/problems/merge-k-sorted-lists/description/ + +문제: k개의 링크드 리스트가 주어지고, 각 링크드 리스트가 오름차순으로 정렬이 되어있다. + 모든 링크드 리스트를 병합하여 하나의 정렬된 링크드 리스트를 만들어라. + +풀이: + heapq를 쓰면 항상 가장 작은 값을 O(log k) 시간에 꺼낼 수 있음. + heapq -> '최소 힙 구조' -> 내부적으로 항상 가장 작은 값이 루트에 오도록 정렬됨. + + 1. 각 리스트의 첫 노드를 heap에 넣음 (val, 고유번호, 노드) + 2. heap에서 가장 작은 값 꺼내면서 결과 리스트 구성 + 3. 다음 노드를 힙에 추가 + +n = 모든 노드의 총 개수 +k = 연결 리스트의 개수 + +TC: O(n log k) +- n개의 노드를 각각 힙에 1번씩 push, 1번씩 pop함 → 총 2n번 힙 연산 +- 각 힙 연산은 log k 시간 (힙 크기 최대 k) + +SC: O(k) +- 힙에 최대 k개의 노드가 동시에 들어감 +""" + +from typing import List, Optional +import heapq + +# 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]: + heap = [] + + # 1. 각 리스트의 첫 노드를 heap에 넣음 (val, 고유번호, 노드) + for idx, node in enumerate(lists): + if node: + heapq.heappush(heap, (node.val, idx, node)) + + dummy = curr = ListNode(-1) + + # 2. heap에서 가장 작은 값 꺼내면서 결과 리스트 구성 + while heap: + val, idx, node = heapq.heappop(heap) # 가장 작은 노드 꺼내기 + curr.next = node # 결과 리스트에 붙이기 + curr = curr.next # 다음 노드로 이동 + + if node.next: + # 다음 노드를 힙에 추가 + heapq.heappush(heap, (node.next.val, idx, node.next)) + + return dummy.next diff --git a/search-in-rotated-sorted-array/hi-rachel.py b/search-in-rotated-sorted-array/hi-rachel.py new file mode 100644 index 000000000..1239eca42 --- /dev/null +++ b/search-in-rotated-sorted-array/hi-rachel.py @@ -0,0 +1,42 @@ +""" +https://leetcode.com/problems/search-in-rotated-sorted-array/solutions/ + +문제: 회전된 오름차순 정렬된 배열 nums에서 target 값의 인덱스를 반환하라. + +Idea: 이진 탐색 패턴 사용 + +TC: O(logN) +SC: O(1) +""" + +from typing import List + +class Solution: + def search(self, nums: List[int], target: int) -> int: + left, right = 0, len(nums) - 1 + + while left <= right: + mid = (left + right) // 2 + + if nums[mid] == target: + return mid + + # 왼쪽이 정렬된 경우 + if nums[left] <= nums[mid]: + # target이 정해진 구간에 있는 경우 + if nums[left] <= target < nums[mid]: + # 왼쪽만 탐색하도록 right를 줄임 + right = mid - 1 + # 아니라면 오른쪽으로 범위를 옮김 + else: + left = mid + 1 + # 오른쪽이 정렬된 경우 + else: + # target이 정해진 구간에 있는 경우 + if nums[mid] < target <= nums[right]: + # 오른쪽만 탐색하도록 left를 늘림 + left = mid + 1 + # 아니라면 왼쪽으로 범위를 옮김 + else: + right = mid - 1 + return -1