Skip to content

[KwonNayeon] Week 10 #1007

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

Merged
merged 10 commits into from
Feb 14, 2025
57 changes: 57 additions & 0 deletions course-schedule/KwonNayeon.py
Original file line number Diff line number Diff line change
@@ -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
Copy link
Contributor

Choose a reason for hiding this comment

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

그리고 재귀 호출 스택의 깊이까지 언급해주면 더 좋을 것 같아요 :)

- 재귀 호출 스택의 깊이는 최악의 경우 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

73 changes: 73 additions & 0 deletions invert-binary-tree/KwonNayeon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""
Constraints:
- The number of nodes in the tree is in the range [0, 100].
- -100 <= Node.val <= 100

<Solution 1>
Time Complexity: O(n)
- 각 노드를 한 번씩 방문함

Space Complexity: O(w)
- w는 트리의 최대 너비(width)

풀이방법:
Copy link
Contributor

Choose a reason for hiding this comment

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

BFS 풀이 방법 좋네요 :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@obzva 님 리뷰 감사합니다! 리뷰해주신 것 참고해서 TC 설명 개선해보겠습니다. 남은 한주 파이팅입니다! 💪😁

1. 큐를 사용한 BFS(너비 우선 탐색)
2. FIFO(First In First Out)로 노드를 처리함
3. 각 노드를 방문할 때마다:
- 왼쪽과 오른쪽 자식 노드의 위치를 교환
- 교환된 자식 노드들을 큐에 추가하여 다음 노드를 처리함

<Solution 2>
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
32 changes: 32 additions & 0 deletions jump-game/KwonNayeon.py
Original file line number Diff line number Diff line change
@@ -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
49 changes: 49 additions & 0 deletions merge-k-sorted-lists/KwonNayeon.py
Original file line number Diff line number Diff line change
@@ -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
52 changes: 52 additions & 0 deletions search-in-rotated-sorted-array/KwonNayeon.py
Original file line number Diff line number Diff line change
@@ -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