Skip to content

Commit

Permalink
Merge pull request #1013 from dusunax/main
Browse files Browse the repository at this point in the history
[οΏ½SunaDu] Week 10
  • Loading branch information
dusunax authored Feb 16, 2025
2 parents 1a97465 + 869e90b commit 68044c0
Show file tree
Hide file tree
Showing 3 changed files with 294 additions and 0 deletions.
184 changes: 184 additions & 0 deletions course-schedule/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
'''
# 207
μ°Έκ³  μ˜μƒ: https://www.youtube.com/watch?v=EgI5nU9etnU
문제 풀이: https://www.algodale.com/problems/course-schedule/
## 문제 정리
πŸ‘‰ prerequisitesλž€? ν•„μˆ˜ μ„ μˆ˜ κ³Όλͺ©μ΄λ‹€.
πŸ‘‰ λ°©ν–₯성이 μžˆλŠ” μ—°κ²° κ΄€κ³„μ΄λ―€λ‘œ, Directed Graphλ‹€.
πŸ‘‰ Cycle λ°œμƒ μ‹œ, μ½”μŠ€λ₯Ό μ΄μˆ˜ν•  수 μ—†λ‹€.(μ„œλ‘œ μ˜μ‘΄ν•˜λŠ” μˆœν™˜μ΄ μžˆμ–΄μ„œ 끝없이 돌게 λ˜λŠ” 경우)
## ν•΄κ²° 방식 두가지
1. BFS, Queue, Topological Sort: μœ„μƒ μ •λ ¬
2. DFS, Cycle Detection: μˆœν™˜ 탐지
### μœ„μƒ μ •λ ¬(Topological Sort) - BFS, Queue
- μ§„μž… 차수(indegree): λ…Έλ“œλ‘œ λ“€μ–΄μ˜€λŠ” ν™”μ‚΄ν‘œ 수
- κ·Έλž˜ν”„λ‘œ 인접 리슀트 ꡬ성
- Queue에 λ„£κΈ°
- Queue BFS 탐색
- λͺ¨λ“  κ³Όλͺ©μ„ λ“€μ—ˆλŠ”μ§€ 확인
### μˆœν™˜ 탐지(Cycle Detection) - DFS
- κ·Έλž˜ν”„λ‘œ 인접 리슀트 ꡬ성
- λ°©λ¬Έ μƒνƒœ λ°°μ—΄ μ΄ˆκΈ°ν™”
- dfs ν•¨μˆ˜
- λͺ¨λ“  λ…Έλ“œμ— λŒ€ν•΄ dfs μ‹€ν–‰
## TC & SC
- μ‹œκ°„ λ³΅μž‘λ„μ™€ 곡간 λ³΅μž‘λ„λŠ” O(V + E)둜 λ™μΌν•˜λ‹€.
```
V: λ…Έλ“œ 수(κ³Όλͺ© 수)
E: κ°„μ„  수(μ„ μˆ˜ κ³Όλͺ© 관계 수)
```
### TC is O(V + E)
두 방법 λͺ¨λ‘, κ·Έλž˜ν”„μ˜ λͺ¨λ“  λ…Έλ“œμ™€ 간선을 ν•œ λ²ˆμ”© 확인함
- BFS: λͺ¨λ“  Vλ₯Ό μˆœνšŒν•˜λ©΄μ„œ, 각 λ…Έλ“œμ—μ„œ λ‚˜κ°€λŠ” Eλ₯Ό 따라가며 차수λ₯Ό μ€„μž„
- DFS: λͺ¨λ“  Vλ₯Ό μˆœνšŒν•˜λ©΄μ„œ, 각 λ…Έλ“œμ—μ„œ μ—°κ²°λœ Eλ₯Ό 따라가며 깊이 탐색
### SC is O(V + E)
- O(V+E): V + Eλ₯Ό μ €μž₯ν•˜λŠ” 인접 리슀트 κ·Έλž˜ν”„
- O(V)'s: λ°©λ¬Έ μƒνƒœ μ €μž₯, μ§„μž… 차수 λ°°μ—΄, BFS 큐, DFS 호좜 μŠ€νƒ
## μœ„μƒμ •λ ¬(BFS) vs μˆœν™˜νƒμ§€(DFS)πŸ€”
### BFSλ₯Ό μ‚¬μš©ν–ˆμ„ λ•Œ
- λ°˜λ³΅λ¬Έμ„ μ‚¬μš©ν•œ BFSκ°€ indegree(μ§„μž…μ°¨μˆ˜) κ°œλ…μ΄ 보닀 μ§κ΄€μ μ΄λ―€λ‘œ => "μˆœμ„œλŒ€λ‘œ μ²˜λ¦¬ν•  수 μžˆλŠ”μ§€ 확인"ν•  λ•Œ λͺ…ν™•ν•˜κ²Œ μ‚¬μš©ν•  수 μžˆλ‹€. μ§„μž… μ°¨μˆ˜κ°€ 0인 λ…Έλ“œλΆ€ν„° μ‹œμž‘ν•΄μ„œ 처리
- μ„ μˆ˜ κ³Όλͺ©μ„ λ‹€ 듀은 κ³Όλͺ©μ€ μ§„μž… μ°¨μˆ˜κ°€ 0이 λ˜λ―€λ‘œ λ“€μ–΄κ°ˆ 수 μžˆλŠ” κ³Όλͺ©μ΄λΌλŠ” 점이 λͺ…확함
```
ν‚€μ›Œλ“œ: 처리 μˆœμ„œλ₯Ό 좜λ ₯, μˆœμ„œλŒ€λ‘œ μ²˜λ¦¬ν•  수 μžˆλŠ”μ§€
```
### DFSλ₯Ό μ‚¬μš©ν–ˆμ„ λ•Œ
- DFS μˆœν™˜ νƒμ§€λŠ” "μˆœν™˜ μ—¬λΆ€"κ°€ 핡심일 λ•Œ μžμ—°μŠ€λŸ½λ‹€.
- μƒνƒœ(Status)λ₯Ό μ‚¬μš©ν•΄μ„œ, λ°©λ¬Έ 쀑인 λ…Έλ“œ μƒνƒœλ₯Ό λ‹€μ‹œ λ°©λ¬Έν•œλ‹€λ©΄ μˆœν™˜μ΄ μžˆμŒμ„ λ°”λ‘œ μ•Œ 수 μžˆλ‹€.
- μˆœν™˜μ΄ 발견되면 λ°”λ‘œ μ€‘λ‹¨ν•˜λ―€λ‘œ, μˆœν™˜ 탐지에 μžμ—°μŠ€λŸ½λ‹€.
```
ν‚€μ›Œλ“œ: 사이클이 μžˆλŠ”μ§€ νŒλ‹¨
```
### +a) `@cache`λ₯Ό ν™œμš©ν•΄λ³΄μž.
- νŒŒμ΄μ„  3.9~ λ©”λͺ¨μ΄μ œμ΄μ…˜ ν•¨μˆ˜
- 순수 ν•¨μˆ˜ + μž¬κ·€ μ΅œμ ν™”μ— μ‚¬μš© (μ™ΈλΆ€ μ˜μ‘΄μ„±, λΆ€μˆ˜νš¨κ³Όμ— μ£Όμ˜ν•  것)
'''
from enum import Enum

class Status(Enum): # use it to dfs
INITIAL = 1
IN_PROGRESS = 2
FINISHED = 3

class Solution:
'''
1. BFS
μœ„μƒ μ •λ ¬
'''
def canFinishTopologicalSort(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
indegree = [0] * numCourses
graph = defaultdict(list)

for dest, src in prerequisites:
graph[src].append(dest)
indegree[dest] += 1

queue = deque([i for i in range(numCourses) if indegree[i] == 0])
processed_count = 0

while queue:
node = queue.popleft()
processed_count += 1
for neighbor in graph[node]:
indegree[neighbor] -= 1
if indegree[neighbor] == 0:
queue.append(neighbor)

return processed_count == numCourses

'''
2. DFS
μˆœν™˜ 탐지
'''
def canFinishCycleDetection(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
graph = defaultdict(list)

for dest, src in prerequisites:
graph[src].append(dest)

statuses = {i: Status.INITIAL for i in range(numCourses)}

def dfs(node):
if statuses[node] == Status.IN_PROGRESS:
return False
if statuses[node] == Status.FINISHED:
return True

statuses[node] = Status.IN_PROGRESS
for neighbor in graph[node]:
if not dfs(neighbor):
return False
statuses[node] = Status.FINISHED
return True

return all(dfs(crs) for crs in range(numCourses))

'''
3. @cache
파이썬 3.9 μ΄μƒμ—μ„œ μ‚¬μš©ν•˜λŠ” λ©”λͺ¨μ΄μ œμ΄μ…˜ λ°μ½”λ ˆμ΄ν„°
- 동일 μž…λ ₯ -> 동일 좜λ ₯을 보μž₯ν•œλ‹€.
- 128개 κΉŒμ§€λ§Œ μ €μž₯ν•˜λŠ” @lru_cache도 μžˆλ‹€.
'''
def canFinishWithCache(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
graph = defaultdict(list)

for dest, src in prerequisites:
graph[src].append(dest)

traversing = set()

@cache
def dfs(node):
if node in traversing:
return False

traversing.add(node)
result = all(dfs(pre) for pre in graph[node])
traversing.remove(node)
return result

return all(dfs(node) for node in range(numCourses))

'''
4. visitedκ³Ό ν•¨κ»˜ μ‚¬μš©ν•˜κΈ°
@cache λ°μ½”λ ˆμ΄ν„°λŠ” λ©”λͺ¨μ΄μ œμ΄μ…˜, 같은 μž…λ ₯값에 따라 같은 κ²°κ³Όλ₯Ό λ°˜ν™˜ν•˜κ²Œν•¨
κ²°κ³Όκ°€ λ³€ν•˜μ§€ μ•Šμ„ λ•Œ μœ μš©ν•¨ => dfs(node)λŠ” μ™ΈλΆ€ μƒνƒœ μˆœν™˜ traversing에 μ˜μ‘΄ν•΄μ„œ λ™μž‘μ΄ λ‹¬λΌμ§ˆ 수 μžˆλ‹€.
λ”°λΌμ„œ visited set이 더 μžμ—°μŠ€λŸ¬μšΈ 수 μžˆλ‹€
'''
def canFinishWithVisited(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
graph = defaultdict(list)

for dest, src in prerequisites:
graph[src].append(dest)

traversing = set()
visited = set()

def dfs(node):
if node in traversing:
return False
if node in visited:
return True

traversing.add(node)
for pre in graph[node]:
if not dfs(pre):
return False
traversing.remove(node)

visited.add(node)
return True

return all(dfs(i) for i in range(numCourses))
65 changes: 65 additions & 0 deletions invert-binary-tree/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
'''
# 226. Invert Binary Tree
switch left and right child of each node
## TC: O(N)
visit each node once
## SC: O(h)
h is height of tree
- best case: O(logN), balanced tree
- worst case: O(N), skewed tree
'''
class Solution:
'''
DFS
'''
def invertTreeRecursive(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

'''
BFS
- 직관적인 stack 풀이
'''
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.append(node.left)
stack.append(node.right)

return root

'''
- 참고용 deque 풀이
'''
def invertTreeDeque(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
dq = deque([root])

while dq:
node = dq.popleft()
if not node:
continue

node.left, node.right = node.right, node.left
dq.append(node.left)
dq.append(node.right)

return root
45 changes: 45 additions & 0 deletions search-in-rotated-sorted-array/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'''
# 33. Search in Rotated Sorted Array
binary search + condition check
이진 탐색 쀑, μ™Όμͺ½μ΄ μ •λ ¬λ˜μ—ˆμ„ λ•Œ
- νƒ€κ²Ÿμ΄ μ •λ ¬λœ μ™Όμͺ½μ— μžˆλŠ” 경우, μ™Όμͺ½ 탐색 (leftλΆ€ν„° mid - 1 μ‚¬μ΄μ—μ„œ νƒ€κ²Ÿμ„ 탐색)
- νƒ€κ²Ÿμ΄ μ •λ ¬λœ μ™Όμͺ½μ— 없을 경우, 였λ₯Έμͺ½ 탐색 (mid + 1λΆ€ν„° right μ‚¬μ΄μ—μ„œ νƒ€κ²Ÿμ„ 탐색)
이진 탐색 쀑, 였λ₯Έμͺ½μ΄ μ •λ ¬λ˜μ—ˆμ„ λ•Œ
- νƒ€κ²Ÿμ΄ μ •λ ¬λœ 였λ₯Έμͺ½μ— μžˆλŠ” 경우, 였λ₯Έμͺ½ 탐색 (mid + 1λΆ€ν„° right μ‚¬μ΄μ—μ„œ νƒ€κ²Ÿμ„ 탐색)
- νƒ€κ²Ÿμ΄ μ •λ ¬λœ 였λ₯Έμͺ½μ— 없을 경우, μ™Όμͺ½ 탐색 (leftλΆ€ν„° mid - 1 μ‚¬μ΄μ—μ„œ νƒ€κ²Ÿμ„ 탐색)
## TC: O(log n)
binary search
## SC: O(1)
no extra space
'''
class Solution:
def search(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums) - 1

while left <= right:
mid = left + (right - left) // 2

if nums[mid] == target:
return mid

if nums[left] <= nums[mid]: # is_left_sorted
if nums[left] <= target < nums[mid]: # is_target_left
right = mid - 1
else:
left = mid + 1
else:
if nums[mid] < target <= nums[right]: # is_target_right
left = mid + 1
else:
right = mid - 1

return -1

0 comments on commit 68044c0

Please sign in to comment.