[alphaorderly] WEEK 12 Solutions - #2854
Conversation
There was a problem hiding this comment.
🏷️ 알고리즘 패턴 분석
non-overlapping-intervals/alphaorderly.py
"""
# 시간 복잡도: O(n log n)
# 공간 복잡도: O(1)
#
# 1. 인터벌을 끝점을 기준으로 오름차순 정렬한다.
# 2. 첫 번째 인터벌의 끝점을 기준으로 순회하면서,
# 다음 인터벌의 시작점이 현재 끝점보다 작으면 겹치는 것으로 간주하여 카운트를 증가시킨다.
# - 겹치면(count += 1) 끝점은 그대로 둔다.
# - 겹치지 않으면 끝점을 현재 인터벌의 끝점으로 업데이트한다.
# 3. 마지막에 카운트를 반환한다.
#
# 왜 최선인가?
# - 끝점이 가장 작은 것부터 남겨두면 이후로 더 많은 인터벌을 남길 수 있다.
# - 이는 최대한 많은 인터벌을 남기기 위해 최소 개수의 인터벌만 제거하게 되는 그리디 전략이다.
"""
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
intervals.sort(key=lambda k: k[1])
N = len(intervals)
count = 0
end = intervals[0][1]
for i in range(1, N):
event_start, event_end = intervals[i]
if event_start < end:
count += 1
else:
end = event_end
return count- 패턴: Greedy
- 설명: 주요 아이디어가 끝점을 기준으로 정렬한 뒤, 겹치는 경우를 최소 제거로 해결하는 그리디 전략으로 구성되어 있습니다. 먼저 끝점이 작은 인터벌을 남겨두고, 겹치면 제거 카운트를 증가시키며 끝점을 유지하거나 업데이트합니다.
📊 시간/공간 복잡도 분석
| 유저 분석 | 실제 분석 | 결과 | |
|---|---|---|---|
| Time | O(n log n) | O(n log n) | ✅ |
| Space | O(1) | O(1) | ✅ |
피드백: 종료점을 오름차순으로 정렬한 뒤 한 번 순회하며 겹치는 경우를 세서 제거 수를 계산한다. 추가 공간은 상수
개선 제안: 현재 구현이 적절해 보입니다.
There was a problem hiding this comment.
🏷️ 알고리즘 패턴 분석
number-of-connected-components-in-an-undirected-graph/alphaorderly.py
"""
# 시간 복잡도: O(n) (Union Find 연산은 경로 압축과 rank 활용으로 Amortized O(1))
# 공간 복잡도: O(n)
#
# Union-Find(유니온 파인드, Disjoint Set Union) 알고리즘을 사용해 무방향 그래프의 연결 요소(connected components) 개수를 계산한다.
#
# 알고리즘 단계:
# 1. 각 노드는 자기 자신을 부모로 갖도록(parent 배열) 초기화한다.
# 2. 트리의 깊이(랭크, rank)를 저장하는 배열을 초기화한다.
# 3. 간선을 하나씩 확인하며 union 연산으로 두 노드를 같은 집합으로 합친다.
# - 이미 같은 집합인 경우엔 아무 작업도 하지 않는다.
# - 서로 다른 집합을 합치면, 연결 요소 개수를 1 감소시킨다.
# 4. 모든 간선을 처리하고 남은 연결 요소 개수(ans)를 반환한다.
"""
class UnionFind:
def __init__(self, n: int):
self.parent = [-1] * n
self.rank = [0] * n
def find(self, target: int) -> int:
if self.parent[target] == -1:
return target
self.parent[target] = self.find(self.parent[target])
return self.parent[target]
def union(self, a: int, b: int) -> bool:
a = self.find(a)
b = self.find(b)
if a == b:
return False
if self.rank[a] < self.rank[b]:
a, b = b, a
self.parent[b] = a
if self.rank[a] == self.rank[b]:
self.rank[a] += 1
return True
class Solution:
def countComponents(self, n: int, edges: List[List[int]]) -> int:
uf = UnionFind(n)
ans = n
for a, b in edges:
if uf.union(a, b):
ans -= 1
return ans- 패턴: Union Find
- 설명: 주어진 코드는 서로의 연결 여부를 관리하는 유니온 파인드(Disjoint Set Union)로 그래프의 연결 요소 개수를 계산한다. 간선을 하나씩 합치며 서로 다른 집합이면 연결 요소를 감소시키는 방식으로 구현된다.
📊 시간/공간 복잡도 분석
| 복잡도 | |
|---|---|
| Time | O(n + m α(n)) |
| Space | O(n) |
피드백: 초기화와 패런트/랭크를 사용해 서로 다른 집합을 합치며 연결 요소 수를 업데이트한다
개선 제안: 현재 구현이 적절해 보입니다.
💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!
There was a problem hiding this comment.
🏷️ 알고리즘 패턴 분석
remove-nth-node-from-end-of-list/alphaorderly.py
"""
# 시간 복잡도: O(n)
# 공간 복잡도: O(1)
#
# 한 번의 순회(one pass)로 연결 리스트의 끝에서 n번째 노드를 제거하는 방법:
# 1. dummy 노드를 만들어 head의 앞에 연결한다.
# 2. forerunner 포인터를 n칸 먼저 이동시킨다.
# 3. forerunner와 lastcomer(초기: dummy)를 같이 한 칸씩 이동하며, forerunner가 끝(None)에 도달하면 lastcomer는 제거할 노드 바로 앞에 위치하게 된다.
# 4. lastcomer.next를 갱신하여 n번째 노드를 제거한다.
"""
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy = ListNode(0)
dummy.next = head
forerunner = head
lastcomer = dummy
for _ in range(n):
forerunner = forerunner.next
while forerunner:
forerunner = forerunner.next
lastcomer = lastcomer.next
lastcomer.next = lastcomer.next.next
return dummy.next- 패턴: Two Pointers, Linked List
- 설명: 목적 노드 제거를 위해 두 포인터를 활용하는 대표적인 패턴으로, 하나를 n칸 먼저 움직이고 이후 같이 한 칸씩 이동시키며 타겟 노드를 찾는다. 연결 리스트의 한 번의 순회로 문제를 해결한다.
📊 시간/공간 복잡도 분석
| 유저 분석 | 실제 분석 | 결과 | |
|---|---|---|---|
| Time | O(n) | O(n) | ✅ |
| Space | O(1) | O(1) | ✅ |
피드백: 더미 헤드 및 두 포인터를 사용해 제거 위치를 찾아 연결을 끊는다
개선 제안: 현재 구현이 적절해 보입니다.
There was a problem hiding this comment.
🏷️ 알고리즘 패턴 분석
same-tree/alphaorderly.py
"""
시간 복잡도 : O(n)
공간 복잡도 : O(n)
재귀적으로 두 이진 트리의 노드를 비교하면서 값이 같은지 확인한다.
"""
class Solution:
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
# 두 노드가 둘다 None 이면 True 를 반환한다.
if p is q:
return True
# 두 노드중 하나가 None 이면 False 를 반환한다.
if not (p and q):
return False
# 둘다 None이 아니라면 값을 비교하고 왼쪽과 오른쪽 서브트리도 재귀적으로 비교한다.
return (
p.val == q.val
and self.isSameTree(p.left, q.left)
and self.isSameTree(p.right, q.right)
)- 패턴: Binary Search, Depth-First Search, Tre e Pattern
- 설명: 추가 설명: 이 코드는 두 트리의 모든 노드를 재귀적으로 비교하여 동일 여부를 확인한다. 각 노드에서 좌우 자식으로 DFS 방식으로 순회하며 조건을 검사한다. 트리 구조를 다룬다는 점에서 DFS의 특성을 띈다.
📊 시간/공간 복잡도 분석
| 복잡도 | |
|---|---|
| Time | O(n) |
| Space | O(h) |
피드백: 두 트리의 동치 여부를 단순 비교하며 좌우 서브트리까지 재귀적으로 확인한다
개선 제안: 현재 구현이 적절해 보입니다.
💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!
📊 alphaorderly 님의 학습 현황이번 주 제출 문제
누적 학습 요약
문제 풀이 현황
🤖 이 댓글은 GitHub App을 통해 자동으로 작성되었습니다. 🔢 API 사용량 (gpt-5-nano)
|
There was a problem hiding this comment.
🏷️ 알고리즘 패턴 분석
serialize-and-deserialize-binary-tree/alphaorderly.py
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
"""
# 시간 복잡도: O(n)
# 공간 복잡도: O(n)
#
# 이 코드는 preorder(전위 순회)를 이용해 이진 트리를 문자열로 직렬화하고,
# 다시 preorder 순서로 문자열을 역직렬화하여 트리를 복원한다.
#
# 1. serialize 함수는 preorder 순회로 트리 노드를 방문하며 값을 문자열로 저장하고,
# None 자리는 특수문자(*)로 표기한다.
# 2. deserialize 함수는 preorder 순서의 문자열을 한 항목씩 읽어가며
# *이면 None, 숫자면 그 값의 TreeNode를 재귀적으로 생성한다.
"""
class Codec:
def serialize(self, root):
ans = []
def preorder(node: Optional[TreeNode]):
if not node:
ans.append("*")
return
ans.append(str(node.val))
preorder(node.left)
preorder(node.right)
preorder(root)
return ','.join(ans)
def deserialize(self, data):
data = iter(data.split(','))
def preorder() -> TreeNode:
current = next(data)
if current == '*':
return None
node = TreeNode(int(current))
node.left = preorder()
node.right = preorder()
return node
return preorder()
# Your Codec object will be instantiated and called as such:
# ser = Codec()
# deser = Codec()
# ans = deser.deserialize(ser.serialize(root))- 패턴: Depth-First Search, Binary Search, Divide and Conquer, Hash Map / Hash Set
- 설명: 전위 순회 기반으로 트리를 방문하며 재귀적으로 노드를 생성/복원하므로 DFS 구성이며, 재귀적 탐색이 핵심이다. 문제 맥락에서 분할된 서브트리 재구성은 Divide and Conquer의 성격도 보인다.
📊 시간/공간 복잡도 분석
| 유저 분석 | 실제 분석 | 결과 | |
|---|---|---|---|
| Time | O(n) | O(n) | ✅ |
| Space | O(n) | O(n) | ✅ |
피드백: None 노드도 '*'로 표기하여 구조를 보존한다
개선 제안: 현재 구현이 적절해 보입니다.
There was a problem hiding this comment.
deserialize 를 재귀로 구현한 부분이 좋네요 !
There was a problem hiding this comment.
회원님 코드도 엄청 인상적이였어요! 깔끔하게 잘 하셨더라구요!
답안 제출 문제
작성자 체크 리스트
In Review로 설정해주세요.검토자 체크 리스트
Important
본인 답안 제출 뿐만 아니라 다른 분 PR 하나 이상을 반드시 검토를 해주셔야 합니다!