diff --git a/course-schedule/KwonNayeon.py b/course-schedule/KwonNayeon.py new file mode 100644 index 000000000..51ffb0697 --- /dev/null +++ b/course-schedule/KwonNayeon.py @@ -0,0 +1,57 @@ +""" +Constraints: +- 1 <= numCourses <= 2000 +- 0 <= prerequisites.length <= 5000 +- prerequisites[i].length == 2 +- 0 <= ai, bi < numCourses +- All the pairs prerequisites[i] are unique. + +Time Complexity: O(N + P) +- N: numCourses, P: prerequisites의 길이 + +Space Complexity: O(N + P) +- 세트의 메모리 사용량이 N과 비례하고 인접 리스트의 크기가 P +- 재귀 호출 스택의 깊이는 최악의 경우 O(N) + +풀이방법: +1. prerequisites을 directed graph로 변환 + - 각 코스별로 선수과목의 리스트를 저장함 +2. DFS를 사용하여 cycle 존재 여부 확인 + - visited 배열: 이미 확인이 완료된 노드 체크 + - path 배열: 현재 DFS 경로에서 방문한 노드 체크 +3. cycle이 발견되면 false, 그렇지 않으면 true 반환 + - 현재 경로에서 이미 방문한 노드를 다시 만나면 cycle 있음 + - 모든 노드 방문이 가능하면 cycle 없음 +""" +class Solution: + def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: + graph = [[] for _ in range(numCourses)] + for course, prereq in prerequisites: + graph[course].append(prereq) + + visited = [False] * numCourses + path = [False] * numCourses + + def dfs(course): + if path[course]: + return False + if visited[course]: + return True + + path[course] = True + + for prereq in graph[course]: + if not dfs(prereq): + return False + + path[course] = False + visited[course] = True + + return True + + for course in range(numCourses): + if not dfs(course): + return False + + return True + diff --git a/invert-binary-tree/KwonNayeon.py b/invert-binary-tree/KwonNayeon.py new file mode 100644 index 000000000..31987ef75 --- /dev/null +++ b/invert-binary-tree/KwonNayeon.py @@ -0,0 +1,73 @@ +""" +Constraints: +- The number of nodes in the tree is in the range [0, 100]. +- -100 <= Node.val <= 100 + + +Time Complexity: O(n) +- 각 노드를 한 번씩 방문함 + +Space Complexity: O(w) +- w는 트리의 최대 너비(width) + +풀이방법: +1. 큐를 사용한 BFS(너비 우선 탐색) +2. FIFO(First In First Out)로 노드를 처리함 +3. 각 노드를 방문할 때마다: + - 왼쪽과 오른쪽 자식 노드의 위치를 교환 + - 교환된 자식 노드들을 큐에 추가하여 다음 노드를 처리함 + + +Time Complexity: O(n) +- 각 노드를 한 번씩 방문함 + +Space Complexity: O(h) +- h는 트리의 높이, 재귀 호출 스택의 최대 깊이 + +풀이방법: +1. DFS(깊이 우선 탐색)와 재귀를 활용 +2. 각 노드에서: + - 왼쪽과 오른쪽 자식 노드의 위치를 교환 + - 재귀적으로 왼쪽, 오른쪽 서브트리에 대해 같은 과정 반복 +3. Base case: root가 None이면 None 반환 +""" +# 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 +from collections import deque + +# Solution 1 (BFS 활용) +class Solution: + def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + if not root: + return None + + queue = deque([root]) + + while queue: + node = queue.popleft() + + node.left, node.right = node.right, node.left + + if node.left: + queue.append(node.left) + if node.right: + queue.append(node.right) + + return root + +# Solution 2 (DFS와 재귀 활용) +class Solution: + def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + if not root: + return None + + root.left, root.right = root.right, root.left + + self.invertTree(root.left) + self.invertTree(root.right) + + return root diff --git a/jump-game/KwonNayeon.py b/jump-game/KwonNayeon.py new file mode 100644 index 000000000..26728ce43 --- /dev/null +++ b/jump-game/KwonNayeon.py @@ -0,0 +1,32 @@ +""" +Constraints: +- 1 <= nums.length <= 10^4 +- 0 <= nums[i] <= 10^5 + +Time Complexity: O(n) +- n은 배열의 길이만큼 한 번 순회 + +Space Complexity: O(1) +- 추가 공간 사용 없음 + +풀이방법: +1. max_reach 변수로 현재까지 도달 가능한 최대 거리 저장 +2. 배열을 순회하면서: + - 현재 위치가 max_reach보다 크면 도달 불가능 + - max_reach를 현재 위치에서 점프 가능한 거리와 비교해 업데이트 + - max_reach가 마지막 인덱스보다 크면 도달 가능 +""" +class Solution: + def canJump(self, nums: List[int]) -> bool: + max_reach = nums[0] + + for i in range(len(nums)): + if i > max_reach: + return False + + max_reach = max(max_reach, i + nums[i]) + + if max_reach >= len(nums) - 1: + return True + + return True diff --git a/merge-k-sorted-lists/KwonNayeon.py b/merge-k-sorted-lists/KwonNayeon.py new file mode 100644 index 000000000..3f518940e --- /dev/null +++ b/merge-k-sorted-lists/KwonNayeon.py @@ -0,0 +1,49 @@ +""" +Constraints: +- k == lists.length +- 0 <= k <= 10^4 +- 0 <= lists[i].length <= 500 +- -10^4 <= lists[i][j] <= 10^4 +- lists[i] is sorted in ascending order. +- The sum of lists[i].length will not exceed 10^4 + +Time Complexity: O(N log k) +- N은 모든 노드의 총 개수, k는 연결 리스트의 개수 +- 힙 연산에 log k 시간이 걸리고, 이를 N번 수행함 + +Space Complexity: O(k) +- 힙에는 항상 k개의 노드만 저장됨 + +풀이방법: +1. 최소 힙을 사용하여 k개의 정렬된 리스트를 효율적으로 병합하는 알고리즘 +2. 각 리스트의 첫 번째 노드를 힙에 넣고 시작함 +3. 힙에서 가장 작은 값을 가진 노드를 꺼내서 결과 리스트에 추가 +4. 꺼낸 노드의 다음 노드를 다시 힙에 넣음 +5. 이 과정을 힙이 빌 때까지 반복함 + +Note: 이 문제는 풀기 어려워서 풀이를 보고 공부했습니다. 복습 필수 +""" +# 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]: + min_heap = [] + + for i, l in enumerate(lists): + if l: + heapq.heappush(min_heap, (l.val, i, l)) + + head = point = ListNode(0) + + while min_heap: + val, i, node = heapq.heappop(min_heap) + point.next = node + point = point.next + + if node.next: + heapq.heappush(min_heap, (node.next.val, i, node.next)) + + return head.next diff --git a/search-in-rotated-sorted-array/KwonNayeon.py b/search-in-rotated-sorted-array/KwonNayeon.py new file mode 100644 index 000000000..71fc53c0e --- /dev/null +++ b/search-in-rotated-sorted-array/KwonNayeon.py @@ -0,0 +1,52 @@ +""" +Constraints: +- 1 <= nums.length <= 5000 +- -10^4 <= nums[i] <= 10^4 +- All values of nums are unique. +- nums is an ascending array that is possibly rotated. +- -10^4 <= target <= 10^4 + +Time Complexity: O(log n) +- Binary Search -> 매 단계마다 탐색 범위가 절반으로 줄어듦 + +Space Complexity: O(1) +- 추가 공간을 사용하지 않음 + +풀이방법: +1. Binary Search + - left와 right 포인터로 탐색 범위 지정 + - mid가 target인지 먼저 확인 +2. 정렬된 부분 찾기 + - mid를 기준으로 왼쪽이 정렬되어 있는지 확인 + - 정렬된 부분에서 target이 존재할 수 있는 범위를 파악 +3. Target 위치 탐색 + - 왼쪽이 정렬되어 있고 target이 그 범위 안에 있다면 오른쪽 범위를 줄임 + - 그렇지 않다면 왼쪽 범위를 늘림 + - 반대의 경우도 동일한 방법 적용 +4. Target을 찾지 못한 경우 -1을 반환함 +""" +class Solution: + def search(self, nums: List[int], target: int) -> int: + left = 0 + right = len(nums) - 1 + + while left <= right: + mid = (left + right) // 2 + + if nums[mid] == target: + return mid + + if nums[left] <= nums[mid]: + if nums[left] <= target <= nums[mid]: + right = mid - 1 + else: + left = mid + 1 + + else: + if nums[mid] < target <= nums[right]: + left = mid + 1 + else: + right = mid - 1 + + return -1 +