diff --git a/find-median-from-data-stream/soobing.ts b/find-median-from-data-stream/soobing.ts new file mode 100644 index 000000000..6c0ef6b1d --- /dev/null +++ b/find-median-from-data-stream/soobing.ts @@ -0,0 +1,131 @@ +/** + * 문제 설명 + * - 중간 값을 찾는 문제 + * + * 아이디어 (👀 어려움..) + * - 최대 힙과 최소 힙을 사용하여 중간 값을 찾는다. + */ +class MinHeap { + heap: number[] = []; + size(): number { + return this.heap.length; + } + peek(): number | null { + return this.heap[0] ?? null; + } + push(val: number) { + this.heap.push(val); + this.bubbleUp(this.size() - 1); + } + pop(): number | null { + if (this.size() === 0) return null; + const top = this.heap[0]; + const end = this.heap.pop()!; + if (this.size() > 0) { + this.heap[0] = end; + this.bubbleDown(0); + } + return top; + } + private bubbleUp(idx: number) { + while (idx > 0) { + const parent = Math.floor((idx - 1) / 2); + if (this.heap[parent] <= this.heap[idx]) break; + [this.heap[parent], this.heap[idx]] = [this.heap[idx], this.heap[parent]]; + idx = parent; + } + } + private bubbleDown(idx: number) { + const n = this.size(); + while (true) { + let left = idx * 2 + 1; + let right = idx * 2 + 2; + let smallest = idx; + if (left < n && this.heap[left] < this.heap[smallest]) smallest = left; + if (right < n && this.heap[right] < this.heap[smallest]) smallest = right; + if (smallest === idx) break; + [this.heap[smallest], this.heap[idx]] = [ + this.heap[idx], + this.heap[smallest], + ]; + idx = smallest; + } + } +} + +class MaxHeap { + heap: number[] = []; + size(): number { + return this.heap.length; + } + peek(): number | null { + return this.heap[0] ?? null; + } + push(val: number) { + this.heap.push(val); + this.bubbleUp(this.size() - 1); + } + pop(): number | null { + if (this.size() === 0) return null; + const top = this.heap[0]; + const end = this.heap.pop()!; + if (this.size() > 0) { + this.heap[0] = end; + this.bubbleDown(0); + } + return top; + } + private bubbleUp(idx: number) { + while (idx > 0) { + const parent = Math.floor((idx - 1) / 2); + if (this.heap[parent] >= this.heap[idx]) break; + [this.heap[parent], this.heap[idx]] = [this.heap[idx], this.heap[parent]]; + idx = parent; + } + } + private bubbleDown(idx: number) { + const n = this.size(); + while (true) { + let left = idx * 2 + 1; + let right = idx * 2 + 2; + let largest = idx; + if (left < n && this.heap[left] > this.heap[largest]) largest = left; + if (right < n && this.heap[right] > this.heap[largest]) largest = right; + if (largest === idx) break; + [this.heap[largest], this.heap[idx]] = [ + this.heap[idx], + this.heap[largest], + ]; + idx = largest; + } + } +} + +class MedianFinder { + private minH = new MinHeap(); + private maxH = new MaxHeap(); + + addNum(num: number): void { + if (this.maxH.size() === 0 || num <= (this.maxH.peek() ?? num)) { + this.maxH.push(num); + } else { + this.minH.push(num); + } + + // Rebalance + if (this.maxH.size() > this.minH.size() + 1) { + this.minH.push(this.maxH.pop()!); + } else if (this.minH.size() > this.maxH.size()) { + this.maxH.push(this.minH.pop()!); + } + } + + findMedian(): number { + const total = this.maxH.size() + this.minH.size(); + if (total % 2 === 1) { + return this.maxH.peek()!; + } else { + return (this.maxH.peek()! + this.minH.peek()!) / 2; + } + } +} diff --git a/insert-interval/soobing.ts b/insert-interval/soobing.ts new file mode 100644 index 000000000..77771e60c --- /dev/null +++ b/insert-interval/soobing.ts @@ -0,0 +1,32 @@ +/** + * 문제 설명 + * - 주어진 시간 간격에 대해 회의를 참석할 수 있는지 여부를 반환하는 문제 + * + * 아이디어 + * 1) 시작 시간을 기준으로 정렬 후, 이전 회의의 종료 시간과 현재 회의의 시작 시간을 비교하여 참석 가능 여부를 판단 + * + */ + +function insert(intervals: number[][], newInterval: number[]): number[][] { + const result: number[][] = []; + let i = 0; + + while (i < intervals.length && newInterval[0] > intervals[i][1]) { + result.push(intervals[i]); + i++; + } + + while (i < intervals.length && newInterval[1] >= intervals[i][0]) { + newInterval[0] = Math.min(intervals[i][0], newInterval[0]); + newInterval[1] = Math.max(intervals[i][1], newInterval[1]); + i++; + } + result.push(newInterval); + + while (i < intervals.length) { + result.push(intervals[i]); + i++; + } + + return result; +} diff --git a/kth-smallest-element-in-a-bst/soobing.ts b/kth-smallest-element-in-a-bst/soobing.ts new file mode 100644 index 000000000..b72dc6353 --- /dev/null +++ b/kth-smallest-element-in-a-bst/soobing.ts @@ -0,0 +1,41 @@ +/** + * 문제 설명 + * - 이진 탐색 트리(BST)가 주어졌을 때, k번째 작은 요소를 찾는 문제 + * + * 아이디어 + * 1) 중위 순회를 하면서 k번째 작은 요소를 찾는다. + * - 이진 탐색 트리(BST)는 중위 순회(in-order traversal)하면 오름차순으로 정렬된 값을 얻을 수 있다. + */ + +class TreeNode { + val: number; + left: TreeNode | null; + right: TreeNode | null; + constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + this.val = val === undefined ? 0 : val; + this.left = left === undefined ? null : left; + this.right = right === undefined ? null : right; + } +} + +function kthSmallest(root: TreeNode | null, k: number): number { + let result = -1; + let count = 0; + + function inOrder(node: TreeNode | null) { + if (!node || result !== -1) return; + + inOrder(node.left); + count++; + + if (count === k) { + result = node.val; + return; + } + + inOrder(node.right); + } + + inOrder(root); + return result; +} diff --git a/lowest-common-ancestor-of-a-binary-search-tree/soobing.ts b/lowest-common-ancestor-of-a-binary-search-tree/soobing.ts new file mode 100644 index 000000000..927e1a596 --- /dev/null +++ b/lowest-common-ancestor-of-a-binary-search-tree/soobing.ts @@ -0,0 +1,32 @@ +/** + * 문제 설명 + * - 이진 탐색 트리의 두 노드의 최소 공통 조상을 찾는 문제 + * + * 아이디어 + * 1) 두 노드의 값을 비교하여 최소 공통 조상을 찾는다. + * - 두 노드의 값이 루트 노드보다 작으면 왼쪽 서브트리로 이동 + * - 두 노드의 값이 루트 노드보다 크면 오른쪽 서브트리로 이동 + * - 두 노드의 값이 루트 노드와 같으면 루트 노드를 반환 + * + */ + +function lowestCommonAncestor( + root: TreeNode | null, + p: TreeNode | null, + q: TreeNode | null +): TreeNode | null { + if (root === null) return null; + if (p === null || q === null) return null; + + while (root !== null) { + if (p.val < root.val && q.val < root.val) { + root = root.left; + } else if (p.val > root.val && q.val > root.val) { + root = root.right; + } else { + return root; + } + } + + return root; +} diff --git a/meeting-rooms/soobing.ts b/meeting-rooms/soobing.ts new file mode 100644 index 000000000..5e407ec55 --- /dev/null +++ b/meeting-rooms/soobing.ts @@ -0,0 +1,35 @@ +/** + * 문제 설명 + * - 주어진 시간 간격에 대해 회의를 참석할 수 있는지 여부를 반환하는 문제 + * + * 아이디어 + * 1) 시작 시간을 기준으로 정렬 후, 이전 회의의 종료 시간과 현재 회의의 시작 시간을 비교하여 참석 가능 여부를 판단 + * + */ + +/** + * Definition of Interval: + * class Interval { + * constructor(start, end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +class Solution { + /** + * @param {Interval[]} intervals + * @returns {boolean} + */ + canAttendMeetings(intervals) { + intervals = intervals.sort((a, b) => a.start - b.start); + + for (let i = 1; i < intervals.length; i++) { + if (intervals[i].start < intervals[i - 1].end) { + return false; + } + } + return true; + } +}