diff --git a/find-median-from-data-stream/Jeehay28.ts b/find-median-from-data-stream/Jeehay28.ts new file mode 100644 index 000000000..30b218eee --- /dev/null +++ b/find-median-from-data-stream/Jeehay28.ts @@ -0,0 +1,194 @@ +class MedianFinder { + // [1, 2, 3, 4, 5, 6] + private lower: MaxHeap; // max heap [3, 2, 1] + private upper: MinHeap; // min heap [4, 5, 6] + + constructor() { + this.upper = new MinHeap(); + this.lower = new MaxHeap(); + } + + // TC: O(log n) + // SC: O(n) + addNum(num: number): void { + if (this.upper.size() === 0 || this.upper.getRoot() <= num) { + this.upper.insert(num); + } else { + this.lower.insert(num); + } + + // Balance + if (this.lower.size() - this.upper.size() > 1) { + this.upper.insert(this.lower.removeRoot()); + } else if (this.upper.size() - this.lower.size() > 1) { + this.lower.insert(this.upper.removeRoot()); + } + } + + // TC: O(1) + // SC: O(1) + findMedian(): number { + if (this.upper.size() === this.lower.size()) { + return (this.upper.getRoot() + this.lower.getRoot()) / 2; + } else if (this.upper.size() > this.lower.size()) { + return this.upper.getRoot(); + } else { + return this.lower.getRoot(); + } + } +} + +/** + * Your MedianFinder object will be instantiated and called as such: + * var obj = new MedianFinder() + * obj.addNum(num) + * var param_2 = obj.findMedian() + */ + +class MinHeap { + private heap: number[] = []; + + size(): number { + return this.heap.length; + } + + getRoot(): number { + return this.heap[0]; + } + + insert(num: number): void { + this.heap.push(num); + this.bubbleUp(); + } + + removeRoot(): number { + const top = this.heap[0]; + const end = this.heap.pop()!; + + if (this.heap.length > 0) { + this.heap[0] = end; + this.bubbleDown(); + } + + return top; + } + + // Fix after removal (downward) + bubbleDown(): void { + let idx = 0; + const length = this.heap.length; + const val = this.heap[0]; + + while (true) { + let leftIdx = 2 * idx + 1; + let rightIdx = 2 * idx + 2; + let swap = idx; + + if (leftIdx < length && this.heap[leftIdx] < this.heap[swap]) { + swap = leftIdx; + } + + if (rightIdx < length && this.heap[rightIdx] < this.heap[swap]) { + swap = rightIdx; + } + + if (swap === idx) break; + + this.heap[idx] = this.heap[swap]; + this.heap[swap] = val; + idx = swap; + } + } + + // Fix after insert (upward) + bubbleUp(): void { + let idx = this.heap.length - 1; + const val = this.heap[idx]; + // Heap as an array + // left child: 2 * i + 1 + // right child: 2 * i + 2 + // parent: Math.floor((i-1) / 2) + + while (idx > 0) { + const parentIdx = Math.floor((idx - 1) / 2); + const parent = this.heap[parentIdx]; + + if (val >= parent) break; + + this.heap[parentIdx] = val; + this.heap[idx] = parent; + idx = parentIdx; + } + } +} + +class MaxHeap { + private heap: number[] = []; + + size(): number { + return this.heap.length; + } + + getRoot(): number { + return this.heap[0]; + } + + insert(num: number): void { + this.heap.push(num); + this.bubbleUp(); + } + + removeRoot(): number { + const top = this.heap[0]; + const end = this.heap.pop()!; + + if (this.heap.length > 0) { + this.heap[0] = end; + this.bubbleDown(); + } + + return top; + } + + bubbleDown(): void { + let idx = 0; + const length = this.heap.length; + const val = this.heap[0]; + + while (true) { + let leftIdx = 2 * idx + 1; + let rightIdx = 2 * idx + 2; + let swap = idx; + + if (leftIdx < length && this.heap[leftIdx] > this.heap[swap]) { + swap = leftIdx; + } + + if (rightIdx < length && this.heap[rightIdx] > this.heap[swap]) { + swap = rightIdx; + } + + if (swap === idx) break; + + this.heap[idx] = this.heap[swap]; + this.heap[swap] = val; + idx = swap; + } + } + + bubbleUp(): void { + let idx = this.heap.length - 1; + const val = this.heap[idx]; + + while (idx > 0) { + const parentIdx = Math.floor((idx - 1) / 2); + const parent = this.heap[parentIdx]; + + if (val <= parent) break; + + this.heap[parentIdx] = val; + this.heap[idx] = parent; + idx = parentIdx; + } + } +} diff --git a/insert-interval/Jeehay28.ts b/insert-interval/Jeehay28.ts new file mode 100644 index 000000000..95c1a3ab7 --- /dev/null +++ b/insert-interval/Jeehay28.ts @@ -0,0 +1,31 @@ +// TC: O(n) +// SC: O(n) +function insert(intervals: number[][], newInterval: number[]): number[][] { + const result: number[][] = []; + const n = intervals.length; + let i = 0; + + // Add all intervals that come before newInterval + while (i < n && intervals[i][1] < newInterval[0]) { + result.push(intervals[i]); + i++; + } + + // Merge all overlapping intervals with newInterval + 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++; + } + + result.push(newInterval); + + // Add remaining intervals after newInterval + while (i < n) { + result.push(intervals[i]); + i++; + } + + return result; +} + diff --git a/kth-smallest-element-in-a-bst/Jeehay28.ts b/kth-smallest-element-in-a-bst/Jeehay28.ts new file mode 100644 index 000000000..860b6ada1 --- /dev/null +++ b/kth-smallest-element-in-a-bst/Jeehay28.ts @@ -0,0 +1,27 @@ +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; + } +} + +// TC: O(n) +// SC: O(n) +function kthSmallest(root: TreeNode | null, k: number): number { + const result: number[] = []; + const dfs = (node: TreeNode | null) => { + if (!node) return; + + dfs(node.left); + result.push(node.val); + dfs(node.right); + }; + + dfs(root); + + return result[k - 1]; +} diff --git a/lowest-common-ancestor-of-a-binary-search-tree/Jeehay28.ts b/lowest-common-ancestor-of-a-binary-search-tree/Jeehay28.ts new file mode 100644 index 000000000..ea0160f90 --- /dev/null +++ b/lowest-common-ancestor-of-a-binary-search-tree/Jeehay28.ts @@ -0,0 +1,53 @@ +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; + } +} + +// TC: O(n) +// SC: O(1) +function lowestCommonAncestor( + root: TreeNode | null, + p: TreeNode | null, + q: TreeNode | null +): TreeNode | null { + if (!root || !p || !q) return null; + + let node = root; + + while (node) { + if (node.val < p.val && node.val < q.val) { + node = node.right!; + } else if (node.val > p.val && node.val > q.val) { + node = node.left!; + } else { + return node; + } + } + + return null; +} + + +// TC: O(n) +// SC: O(n) +// function lowestCommonAncestor( +// root: TreeNode | null, +// p: TreeNode | null, +// q: TreeNode | null +// ): TreeNode | null { +// if (!root || !p || !q) return null; + +// if (p.val < root.val && q.val < root.val) { +// return lowestCommonAncestor(root.left, p, q); +// } else if (p.val > root.val && q.val > root.val) { +// return lowestCommonAncestor(root.right, p, q); +// } else { +// return root; +// } +// }