From 0cac92320677c686d0b2c36d0b33a5254090fd2b Mon Sep 17 00:00:00 2001 From: hi-rachel Date: Tue, 24 Jun 2025 18:15:36 +0900 Subject: [PATCH 1/7] insert-interval solution (py) --- insert-interval/hi-rachel.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 insert-interval/hi-rachel.py diff --git a/insert-interval/hi-rachel.py b/insert-interval/hi-rachel.py new file mode 100644 index 000000000..6a35f9452 --- /dev/null +++ b/insert-interval/hi-rachel.py @@ -0,0 +1,35 @@ +# https://leetcode.com/problems/insert-interval/ +# +# 새로운 구간을 기존의 정렬된 구간 리스트에 삽입하고, 겹치는 구간을 병합 +# 오름차순 정렬된 [start, end] 형태의 리스트. +# 새로운 interval [newStart, newEnd]를 삽입한 후에도 구간이 겹치지 않고 정렬된 상태로 유지. +# +# TC: O(N), 각 interval을 최대 한 번씩만 탐색 +# SC: O(N) + +from typing import List + +class Solution: + def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]: + res = [] + i = 0 + n = len(intervals) + + # 1. 왼쪽에 있는 겹치지 않는 구간 + while i < n and intervals[i][1] < newInterval[0]: + res.append(intervals[i]) + i += 1 + + # 2. 겹치는 구간 병합 + while i < n and intervals[i][0] <= newInterval[1]: + newInterval[0] = min(newInterval[0], intervals[i][0]) + newInterval[1] = max(newInterval[1], intervals[i][1]) + i += 1 + res.append(newInterval) + + # 3. 오른쪽에 있는 겹치지 않는 구간 + while i < n: + res.append(intervals[i]) + i += 1 + + return res From 97cade6e9e1807f8cd226e744f58421ec21f524c Mon Sep 17 00:00:00 2001 From: hi-rachel Date: Tue, 24 Jun 2025 18:28:16 +0900 Subject: [PATCH 2/7] insert-interval solution (ts) --- insert-interval/hi-rachel.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 insert-interval/hi-rachel.ts diff --git a/insert-interval/hi-rachel.ts b/insert-interval/hi-rachel.ts new file mode 100644 index 000000000..35977b21c --- /dev/null +++ b/insert-interval/hi-rachel.ts @@ -0,0 +1,25 @@ +function insert(intervals: number[][], newInterval: number[]): number[][] { + const res: number[][] = []; + let i = 0; + const n = intervals.length; + + // 1. newInterval보다 왼쪽에 있는 interval은 그대로 추가 + while (i < n && intervals[i][1] < newInterval[0]) { + res.push(intervals[i]); + i++; + } + // 2. 겹치는 구간 병합 + while (i < n && intervals[i][0] <= newInterval[1]) { + newInterval[0] = Math.min(intervals[i][0], newInterval[0]); + newInterval[1] = Math.max(intervals[i][1], newInterval[1]); + i++; + } + res.push(newInterval); + + // 3. 나머지 오른쪽 구간 추가 + while (i < n) { + res.push(intervals[i]); + i++; + } + return res; +} From eb91f799d6326aadec9035fe0b903dea47ce3601 Mon Sep 17 00:00:00 2001 From: hi-rachel Date: Wed, 25 Jun 2025 23:51:02 +0900 Subject: [PATCH 3/7] lowest-common-ancestor-of-a-binary-search-tree solution (py) --- .../hi-rachel.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 lowest-common-ancestor-of-a-binary-search-tree/hi-rachel.py diff --git a/lowest-common-ancestor-of-a-binary-search-tree/hi-rachel.py b/lowest-common-ancestor-of-a-binary-search-tree/hi-rachel.py new file mode 100644 index 000000000..7e0994d1e --- /dev/null +++ b/lowest-common-ancestor-of-a-binary-search-tree/hi-rachel.py @@ -0,0 +1,48 @@ +# 주어진 BST와 노드 p, q가 주어질 때 +# 이 둘의 가장 가까운 공통 조상(Lowest Common Ancestor, LCA)을 찾아 반환해라 +# 공통 조상이란, p와 q 모두의 조상 중에서 가장 깊은 노드를 의미 +# +# BST(Binary Search Tree)의 특징 +# - 각 노드는 최대 두 개의 자식 노드를 가질 수 있음 +# - 왼쪽 서브트리의 모든 값은 현재 노드보다 작고, +# - 오른쪽 서브트리의 모든 값은 현재 노드보다 큼. +# +# 풀이 +# 현재 노드가 p, q보다 둘 다 작으면, 오른쪽 서브트리에서 LCA를 찾고, +# 둘 다 크면, 왼쪽 서브 트리에서 찾고, +# 한쪽은 작고, 한쪽은 크면 현재 노드가 LCA가 됨. + +# TC: O(H), H는 이진 검색 트리의 높이 +# SC: O(H), 재귀 스택에 필요한 공간 + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + +# 재귀 풀이 +# TC: O(H), H는 이진 검색 트리의 높이 +# SC: O(H), 재귀 스택에 필요한 공간 +class Solution: + def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': + if p.val < root.val and q.val < root.val: + return self.lowestCommonAncestor(root.left, p, q) + if root.val < p.val and root.val < q.val: + return self.lowestCommonAncestor(root.right, p, q) + return root + +# 반복 풀이 +# TC: O(H), H는 이진 검색 트리의 높이 +# SC: O(1) +class Solution: + def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': + node = root + while node: + if p.val < node.val and q.val < node.val: + node = node.left + elif node.val < p.val and node.val < q.val: + node = node.right + else: + return node From ab643238e32355c9dbe67023723710b798c11f06 Mon Sep 17 00:00:00 2001 From: hi-rachel Date: Thu, 26 Jun 2025 15:09:39 +0900 Subject: [PATCH 4/7] kth-smallest-element-in-a-bst solution (py) --- kth-smallest-element-in-a-bst/hi-rachel.py | 83 ++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 kth-smallest-element-in-a-bst/hi-rachel.py diff --git a/kth-smallest-element-in-a-bst/hi-rachel.py b/kth-smallest-element-in-a-bst/hi-rachel.py new file mode 100644 index 000000000..575c73a10 --- /dev/null +++ b/kth-smallest-element-in-a-bst/hi-rachel.py @@ -0,0 +1,83 @@ +# BST에서 k번째로 작은 value를 반환해라 +# 인덱스는 1에서 시작 + +from typing import Optional + +# 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 + +# 풀이 1. Sort +# 모든 노드의 값을 오름차순으로 정렬 후에 k번째 값을 구한다. +# TC: O(N log N), N은 노드의 개수, 트리 순회 O(N), 정렬 O(N log N) +# SC: O(N), 모든 노드의 값을 저장하는 리스트 + +class Solution: + def kthSmallest(self, root: Optional[TreeNode], k: int) -> int: + values = [] + + def dfs(node): + if not node: + return + + values.append(node.val) + + dfs(node.left) + dfs(node.right) + + dfs(root) + + return sorted(values)[k - 1] + +# 풀이 2. 힙 활용 +# k번째로 작은 값 구하기 -> 최대 힙 활용 +# - 요소를 -node.val 음수로 변환해 최대 힙 효과를 낸다. +# - 최대 힙에 값을 추가하다가, k개가 초과하면 최대 힙으로부터 최댓값을 제거 +# => 트리 순회 종료 후, 최대 힙에는 가장 작은 k개 값만 남게 된다. +# TC: O(N log K), N은 노드의 개수, K는 찾고자 하는 값의 순서, 트리 순회 O(N), 최대 힙 삽입/제거 O(logK) +# SC: O(K), 최대 k개의 값을 저장하는 최대 힙 + +from heapq import heappush, heappop + +class Solution: + def kthSmallest(self, root: Optional[TreeNode], k: int) -> int: + heap = [] # 최대 k개의 작은 값을 저장할 최대 힙 + + def dfs(node): + if not node: + return + + heappush(heap, -node.val) + if len(heap) > k: + heappop(heap) + + dfs(node.left) + dfs(node.right) + + dfs(root) + + # 힙의 최댓값이 k번째로 작은 값 + return -heap[0] + +# 풀이 3. 중위 순회 활용 +# BST는 중위 순회를 하면 오름차순으로 노드에 접근할 수 있다 +# => 입력 트리를 중위 순회 하면서 노드 값을 배열에 저장하면 자연스럽게 배열은 정렬됨 +# TC: O(N), N은 노드의 개수, 트리 순회 O(N) +# SC: O(N), 모든 노드의 값을 저장하는 리스트 + +class Solution: + def kthSmallest(self, root: Optional[TreeNode], k: int) -> int: + values = [] + + def dfs(node): + if not node: + return + dfs(node.left) + values.append(node.val) + dfs(node.right) + + dfs(root) + return values[k - 1] From 82d09211aba9d3c6861ca5dc8702cbb451122feb Mon Sep 17 00:00:00 2001 From: hi-rachel Date: Fri, 27 Jun 2025 22:43:36 +0900 Subject: [PATCH 5/7] meeting-rooms solution (py) --- meeting-rooms/hi-rachel.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 meeting-rooms/hi-rachel.py diff --git a/meeting-rooms/hi-rachel.py b/meeting-rooms/hi-rachel.py new file mode 100644 index 000000000..93cc5562d --- /dev/null +++ b/meeting-rooms/hi-rachel.py @@ -0,0 +1,22 @@ +# https://leetcode.com/problems/meeting-rooms/ +# 주어진 회의 시간 리스트에서 모든 회의가 겹치지 않고 진행될 수 있는지 확인 +# 회의 시간은 [start, end] 형태의 리스트로 주어짐 +# 회의 시간이 겹치는 경우 false, 겹치지 않는 경우 true 반환 + +# TC: O(N log N), N은 회의의 개수, 회의 시간 정렬 O(N log N) +# SC: O(1) + +from typing import List + +class Solution: + def canAttendMeetings(self, intervals: List[List[int]]) -> bool: + intervals.sort(key=lambda x: x[0]) + for i in range(1, len(intervals)): + # 현재 회의의 시작 시간이 이전 회의의 종료 시간보다 작으면 겹침 + if intervals[i][0] < intervals[i - 1][1]: + return False + return True + +# sol = Solution() +# print(sol.canAttendMeetings([[0, 30], [5, 10], [15, 20]])) +# print(sol.canAttendMeetings([[7, 10], [2, 4]])) From 39da955df6b8f1dd23f38ab23e0e6b1c3aa199d6 Mon Sep 17 00:00:00 2001 From: hi-rachel Date: Fri, 27 Jun 2025 22:45:46 +0900 Subject: [PATCH 6/7] meeting-rooms solution (ts) --- meeting-rooms/hi-rachel.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 meeting-rooms/hi-rachel.ts diff --git a/meeting-rooms/hi-rachel.ts b/meeting-rooms/hi-rachel.ts new file mode 100644 index 000000000..4ab43f437 --- /dev/null +++ b/meeting-rooms/hi-rachel.ts @@ -0,0 +1,13 @@ +// https://leetcode.com/problems/meeting-rooms/ +// TC: O(N log N), N은 회의의 개수, 회의 시간 정렬 O(N log N) +// SC: O(1) + +function canAttendMeetings(intervals: number[][]): boolean { + intervals.sort((a, b) => a[0] - b[0]); + for (let i = 1; i < intervals.length; i++) { + if (intervals[i][0] < intervals[i - 1][1]) { + return false; + } + } + return true; +} From cc30a1fb18b30d290f011acc10b46b01544ac54f Mon Sep 17 00:00:00 2001 From: hi-rachel Date: Sat, 28 Jun 2025 13:55:23 +0900 Subject: [PATCH 7/7] find-median-from-data-stream solution (py) --- find-median-from-data-stream/hi-rachel.py | 39 +++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 find-median-from-data-stream/hi-rachel.py diff --git a/find-median-from-data-stream/hi-rachel.py b/find-median-from-data-stream/hi-rachel.py new file mode 100644 index 000000000..ad4131f9e --- /dev/null +++ b/find-median-from-data-stream/hi-rachel.py @@ -0,0 +1,39 @@ +# 두 개로 나눠서 저장하면, +# 홀수개의 숫자일 경우: 첫 번째에서 가장 큰 값이나 두 번째에서 가장 작은 값이 중앙값 +# 짝수개의 숫자일 경우: 첫 번째에서 가장 큰 값과 두 번째에서 가장 작은 값의 평균 +# => 힙 활용 +# 숫자 추가시 작은 값들 => 최대 힙, 큰 값들 => 최소 힙에 음수로 저장! 최대 힙의 모든 값 ≤ 최소 힙의 모든 값 +# 최대 힙에서 가장 큰 값을 최소 힙으로 이동 +# 만약 최소 힙 크기가 더 크면 다시 하나를 최대 힙으로 이동 +# 두 힙간의 크기 차이 항상 0 또는 1 유지 +# TC: addNum O(log n), findMedian O(1) +# SC: O(n) + +import heapq + +class MedianFinder: + def __init__(self): + self.max_heap = [] # 최대 힙 (음수로 저장) + self.min_heap = [] # 최소 힙 + + def addNum(self, num: int) -> None: + # 1. 최대 힙에 먼저 삽입 (음수로) + heapq.heappush(self.max_heap, -num) + + # 2. max_heap에서 가장 큰 값을 min_heap으로 옮김 (오름차순 정렬 유지) + heapq.heappush(self.min_heap, -heapq.heappop(self.max_heap)) + + # 3. min_heap이 max_heap보다 커지면, 다시 max_heap으로 옮김 (균형 유지) + if len(self.min_heap) > len(self.max_heap): + heapq.heappush(self.max_heap, -heapq.heappop(self.min_heap)) + + def findMedian(self) -> float: + if len(self.max_heap) == len(self.min_heap): + return (-self.max_heap[0] + self.min_heap[0]) / 2 + else: + return -self.max_heap[0] + +# Your MedianFinder object will be instantiated and called as such: +# obj = MedianFinder() +# obj.addNum(num) +# param_2 = obj.findMedian()