Skip to content

Commit ce944e1

Browse files
authored
Merge pull request #1622 from hi-rachel/main
[hi-rachel] Week 13 solutions
2 parents e6255ee + cc30a1f commit ce944e1

File tree

7 files changed

+265
-0
lines changed

7 files changed

+265
-0
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# 두 개로 나눠서 저장하면,
2+
# 홀수개의 숫자일 경우: 첫 번째에서 가장 큰 값이나 두 번째에서 가장 작은 값이 중앙값
3+
# 짝수개의 숫자일 경우: 첫 번째에서 가장 큰 값과 두 번째에서 가장 작은 값의 평균
4+
# => 힙 활용
5+
# 숫자 추가시 작은 값들 => 최대 힙, 큰 값들 => 최소 힙에 음수로 저장! 최대 힙의 모든 값 ≤ 최소 힙의 모든 값
6+
# 최대 힙에서 가장 큰 값을 최소 힙으로 이동
7+
# 만약 최소 힙 크기가 더 크면 다시 하나를 최대 힙으로 이동
8+
# 두 힙간의 크기 차이 항상 0 또는 1 유지
9+
# TC: addNum O(log n), findMedian O(1)
10+
# SC: O(n)
11+
12+
import heapq
13+
14+
class MedianFinder:
15+
def __init__(self):
16+
self.max_heap = [] # 최대 힙 (음수로 저장)
17+
self.min_heap = [] # 최소 힙
18+
19+
def addNum(self, num: int) -> None:
20+
# 1. 최대 힙에 먼저 삽입 (음수로)
21+
heapq.heappush(self.max_heap, -num)
22+
23+
# 2. max_heap에서 가장 큰 값을 min_heap으로 옮김 (오름차순 정렬 유지)
24+
heapq.heappush(self.min_heap, -heapq.heappop(self.max_heap))
25+
26+
# 3. min_heap이 max_heap보다 커지면, 다시 max_heap으로 옮김 (균형 유지)
27+
if len(self.min_heap) > len(self.max_heap):
28+
heapq.heappush(self.max_heap, -heapq.heappop(self.min_heap))
29+
30+
def findMedian(self) -> float:
31+
if len(self.max_heap) == len(self.min_heap):
32+
return (-self.max_heap[0] + self.min_heap[0]) / 2
33+
else:
34+
return -self.max_heap[0]
35+
36+
# Your MedianFinder object will be instantiated and called as such:
37+
# obj = MedianFinder()
38+
# obj.addNum(num)
39+
# param_2 = obj.findMedian()

insert-interval/hi-rachel.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# https://leetcode.com/problems/insert-interval/
2+
#
3+
# 새로운 구간을 기존의 정렬된 구간 리스트에 삽입하고, 겹치는 구간을 병합
4+
# 오름차순 정렬된 [start, end] 형태의 리스트.
5+
# 새로운 interval [newStart, newEnd]를 삽입한 후에도 구간이 겹치지 않고 정렬된 상태로 유지.
6+
#
7+
# TC: O(N), 각 interval을 최대 한 번씩만 탐색
8+
# SC: O(N)
9+
10+
from typing import List
11+
12+
class Solution:
13+
def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
14+
res = []
15+
i = 0
16+
n = len(intervals)
17+
18+
# 1. 왼쪽에 있는 겹치지 않는 구간
19+
while i < n and intervals[i][1] < newInterval[0]:
20+
res.append(intervals[i])
21+
i += 1
22+
23+
# 2. 겹치는 구간 병합
24+
while i < n and intervals[i][0] <= newInterval[1]:
25+
newInterval[0] = min(newInterval[0], intervals[i][0])
26+
newInterval[1] = max(newInterval[1], intervals[i][1])
27+
i += 1
28+
res.append(newInterval)
29+
30+
# 3. 오른쪽에 있는 겹치지 않는 구간
31+
while i < n:
32+
res.append(intervals[i])
33+
i += 1
34+
35+
return res

insert-interval/hi-rachel.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
function insert(intervals: number[][], newInterval: number[]): number[][] {
2+
const res: number[][] = [];
3+
let i = 0;
4+
const n = intervals.length;
5+
6+
// 1. newInterval보다 왼쪽에 있는 interval은 그대로 추가
7+
while (i < n && intervals[i][1] < newInterval[0]) {
8+
res.push(intervals[i]);
9+
i++;
10+
}
11+
// 2. 겹치는 구간 병합
12+
while (i < n && intervals[i][0] <= newInterval[1]) {
13+
newInterval[0] = Math.min(intervals[i][0], newInterval[0]);
14+
newInterval[1] = Math.max(intervals[i][1], newInterval[1]);
15+
i++;
16+
}
17+
res.push(newInterval);
18+
19+
// 3. 나머지 오른쪽 구간 추가
20+
while (i < n) {
21+
res.push(intervals[i]);
22+
i++;
23+
}
24+
return res;
25+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# BST에서 k번째로 작은 value를 반환해라
2+
# 인덱스는 1에서 시작
3+
4+
from typing import Optional
5+
6+
# Definition for a binary tree node.
7+
class TreeNode:
8+
def __init__(self, val=0, left=None, right=None):
9+
self.val = val
10+
self.left = left
11+
self.right = right
12+
13+
# 풀이 1. Sort
14+
# 모든 노드의 값을 오름차순으로 정렬 후에 k번째 값을 구한다.
15+
# TC: O(N log N), N은 노드의 개수, 트리 순회 O(N), 정렬 O(N log N)
16+
# SC: O(N), 모든 노드의 값을 저장하는 리스트
17+
18+
class Solution:
19+
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
20+
values = []
21+
22+
def dfs(node):
23+
if not node:
24+
return
25+
26+
values.append(node.val)
27+
28+
dfs(node.left)
29+
dfs(node.right)
30+
31+
dfs(root)
32+
33+
return sorted(values)[k - 1]
34+
35+
# 풀이 2. 힙 활용
36+
# k번째로 작은 값 구하기 -> 최대 힙 활용
37+
# - 요소를 -node.val 음수로 변환해 최대 힙 효과를 낸다.
38+
# - 최대 힙에 값을 추가하다가, k개가 초과하면 최대 힙으로부터 최댓값을 제거
39+
# => 트리 순회 종료 후, 최대 힙에는 가장 작은 k개 값만 남게 된다.
40+
# TC: O(N log K), N은 노드의 개수, K는 찾고자 하는 값의 순서, 트리 순회 O(N), 최대 힙 삽입/제거 O(logK)
41+
# SC: O(K), 최대 k개의 값을 저장하는 최대 힙
42+
43+
from heapq import heappush, heappop
44+
45+
class Solution:
46+
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
47+
heap = [] # 최대 k개의 작은 값을 저장할 최대 힙
48+
49+
def dfs(node):
50+
if not node:
51+
return
52+
53+
heappush(heap, -node.val)
54+
if len(heap) > k:
55+
heappop(heap)
56+
57+
dfs(node.left)
58+
dfs(node.right)
59+
60+
dfs(root)
61+
62+
# 힙의 최댓값이 k번째로 작은 값
63+
return -heap[0]
64+
65+
# 풀이 3. 중위 순회 활용
66+
# BST는 중위 순회를 하면 오름차순으로 노드에 접근할 수 있다
67+
# => 입력 트리를 중위 순회 하면서 노드 값을 배열에 저장하면 자연스럽게 배열은 정렬됨
68+
# TC: O(N), N은 노드의 개수, 트리 순회 O(N)
69+
# SC: O(N), 모든 노드의 값을 저장하는 리스트
70+
71+
class Solution:
72+
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
73+
values = []
74+
75+
def dfs(node):
76+
if not node:
77+
return
78+
dfs(node.left)
79+
values.append(node.val)
80+
dfs(node.right)
81+
82+
dfs(root)
83+
return values[k - 1]
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# 주어진 BST와 노드 p, q가 주어질 때
2+
# 이 둘의 가장 가까운 공통 조상(Lowest Common Ancestor, LCA)을 찾아 반환해라
3+
# 공통 조상이란, p와 q 모두의 조상 중에서 가장 깊은 노드를 의미
4+
#
5+
# BST(Binary Search Tree)의 특징
6+
# - 각 노드는 최대 두 개의 자식 노드를 가질 수 있음
7+
# - 왼쪽 서브트리의 모든 값은 현재 노드보다 작고,
8+
# - 오른쪽 서브트리의 모든 값은 현재 노드보다 큼.
9+
#
10+
# 풀이
11+
# 현재 노드가 p, q보다 둘 다 작으면, 오른쪽 서브트리에서 LCA를 찾고,
12+
# 둘 다 크면, 왼쪽 서브 트리에서 찾고,
13+
# 한쪽은 작고, 한쪽은 크면 현재 노드가 LCA가 됨.
14+
15+
# TC: O(H), H는 이진 검색 트리의 높이
16+
# SC: O(H), 재귀 스택에 필요한 공간
17+
18+
# Definition for a binary tree node.
19+
class TreeNode:
20+
def __init__(self, x):
21+
self.val = x
22+
self.left = None
23+
self.right = None
24+
25+
# 재귀 풀이
26+
# TC: O(H), H는 이진 검색 트리의 높이
27+
# SC: O(H), 재귀 스택에 필요한 공간
28+
class Solution:
29+
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
30+
if p.val < root.val and q.val < root.val:
31+
return self.lowestCommonAncestor(root.left, p, q)
32+
if root.val < p.val and root.val < q.val:
33+
return self.lowestCommonAncestor(root.right, p, q)
34+
return root
35+
36+
# 반복 풀이
37+
# TC: O(H), H는 이진 검색 트리의 높이
38+
# SC: O(1)
39+
class Solution:
40+
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
41+
node = root
42+
while node:
43+
if p.val < node.val and q.val < node.val:
44+
node = node.left
45+
elif node.val < p.val and node.val < q.val:
46+
node = node.right
47+
else:
48+
return node

meeting-rooms/hi-rachel.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# https://leetcode.com/problems/meeting-rooms/
2+
# 주어진 회의 시간 리스트에서 모든 회의가 겹치지 않고 진행될 수 있는지 확인
3+
# 회의 시간은 [start, end] 형태의 리스트로 주어짐
4+
# 회의 시간이 겹치는 경우 false, 겹치지 않는 경우 true 반환
5+
6+
# TC: O(N log N), N은 회의의 개수, 회의 시간 정렬 O(N log N)
7+
# SC: O(1)
8+
9+
from typing import List
10+
11+
class Solution:
12+
def canAttendMeetings(self, intervals: List[List[int]]) -> bool:
13+
intervals.sort(key=lambda x: x[0])
14+
for i in range(1, len(intervals)):
15+
# 현재 회의의 시작 시간이 이전 회의의 종료 시간보다 작으면 겹침
16+
if intervals[i][0] < intervals[i - 1][1]:
17+
return False
18+
return True
19+
20+
# sol = Solution()
21+
# print(sol.canAttendMeetings([[0, 30], [5, 10], [15, 20]]))
22+
# print(sol.canAttendMeetings([[7, 10], [2, 4]]))

meeting-rooms/hi-rachel.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// https://leetcode.com/problems/meeting-rooms/
2+
// TC: O(N log N), N은 회의의 개수, 회의 시간 정렬 O(N log N)
3+
// SC: O(1)
4+
5+
function canAttendMeetings(intervals: number[][]): boolean {
6+
intervals.sort((a, b) => a[0] - b[0]);
7+
for (let i = 1; i < intervals.length; i++) {
8+
if (intervals[i][0] < intervals[i - 1][1]) {
9+
return false;
10+
}
11+
}
12+
return true;
13+
}

0 commit comments

Comments
 (0)