diff --git a/javascript/215-kth-largest-element-in-array.js b/javascript/215-kth-largest-element-in-array.js new file mode 100644 index 000000000..dd3c8c29b --- /dev/null +++ b/javascript/215-kth-largest-element-in-array.js @@ -0,0 +1,51 @@ +/* + * Using Quick Select Algorithm + * https://www.youtube.com/watch?v=XEmy13g1Qxc + * https://leetcode.com/problems/kth-largest-element-in-an-array/ + */ +const findKthLargest = (nums, k) => { + // find k distance from 0 index + k = nums.length - k; + // similar to quick sort + return quickSelect(nums, 0, nums.length - 1, k); +}; + + +// recursive function +function quickSelect (nums, left, right, k) { + let pivot = nums[right]; + let p = left; + for (let i = left; i < right; i += 1) { + if (nums[i] <= pivot) { + // swap + [nums[p], nums[i]] = [nums[i], nums[p]]; + p += 1; + } + } + // swap + [nums[p], nums[right]] = [nums[right], nums[p]]; + if (p > k) { + return quickSelect(nums, left, p - 1, k); + } else { + if (p < k) { + return quickSelect(nums, p + 1, right, k); + } else { + return nums[p]; + } + } +} + +// TESTING +console.log('Kth Largest element is:', findKthLargest([3,2,1,5,6,4], 2)); // 5 +console.log('Kth Largest element is:', findKthLargest([3,2,3,1,2,4,5,5,6], 4)); // 4 + +/** + * NOTES + * Three approaches + * Approach #1 - Sort the array get the kth largest from behind, O(nlogn) + * Approach #2 - Using MaxHeap - O(n) + O(klogn) + * Approach #3 - Using Quick Select Algorithm + * - Has an average Time complexity: O(n) + * - Has a worst time complexity: O(n^2) + * - For explanation, watch the video + */ diff --git a/javascript/295-find-median-from-data-stream.js b/javascript/295-find-median-from-data-stream.js new file mode 100644 index 000000000..134c1c802 --- /dev/null +++ b/javascript/295-find-median-from-data-stream.js @@ -0,0 +1,173 @@ +/** + * Find Median in a Datastream + * https://www.youtube.com/watch?v=itmhHWaHupI + * https://leetcode.com/problems/find-median-from-data-stream/ + * Using Heap Datastructure (Maxheap and Minheap) + */ +class MedianFinder { + constructor () { + // create 2 heaps + this.smallHeap = new Heap((a,b)=>(b-a)); //maxHeap + this.largeHeap = new Heap((a,b)=>(a-b)); //minHeap + } + + addNum (num) { + this.smallHeap.add(num); + + // make sure every num small is <= every num in large + if (this.smallHeap.size() > 0 && + this.largeHeap.size() > 0 && + this.smallHeap.peek() > + this.largeHeap.peek()) { + const el = this.smallHeap.remove(); + this.largeHeap.add(el); + } + + // check uneven size + if (this.smallHeap.size() > this.largeHeap.size()+1) { + const el = this.smallHeap.remove(); + this.largeHeap.add(el); + } + if (this.largeHeap.size() > this.smallHeap.size()+1) { + const el = this.largeHeap.remove(); + this.smallHeap.add(el); + } + } + + findMedian () { + // check if we have odd number of elements + if (this.smallHeap.size() > this.largeHeap.size()) { + return this.smallHeap.peek(); + } else if (this.largeHeap.size() > this.smallHeap.size()) { + return this.largeHeap.peek(); + } + return ((this.smallHeap.peek() + this.largeHeap.peek())/2); + } +} + +/** + * egghead.io - Heap + * https://github.com/basarat/algorithms/blob/master/src/heap/heap.ts + */ + +/** + * Implements the heap data structure + * A heap is used as a priority queue + * Note: The default compare behavior gives you a min heap + * @constructor ((a,b) => void) comparator, by default - ascending (a-b) + */ +class Heap { + + + constructor(compareFn = undefined) { + this.data = []; + this.compareFn = compareFn && typeof compareFn === 'function' ? compareFn : (a, b) => (a - b); + } + + left(nodeIndex) { + return (2 * nodeIndex) + 1; + } + + right(nodeIndex) { + return (2 * nodeIndex) + 2; + } + + parent(nodeIndex) { + return nodeIndex % 2 == 0 + ? (nodeIndex - 2) / 2 + : (nodeIndex - 1) / 2; + } + + /** + * Adds the given element into the heap in O(logn) + */ + add(element) { + this.data.push(element); + this.siftUp(this.data.length - 1); + } + + /** + * Moves the nod at the given index up to its proper place in the heap. + * @param index The index of the node to move up. + */ + siftUp(index) { + let parent = this.parent(index); + while (index > 0 && this.compareFn(this.data[parent], this.data[index]) > 0) { + [this.data[parent], this.data[index]] = [this.data[index], this.data[parent]]; + index = parent; + parent = this.parent(index); + } + } + + /** + * Retrieves and removes the root element of this heap in O(logn) + * - Returns undefined if the heap is empty. + */ + remove() { + if (this.data.length > 0) { + const root = this.data[0]; + const last = this.data.pop(); + if (this.data.length > 0) { + this.data[0] = last; + this.siftDown(0); + } + return root; + } else { + return undefined; + } + } + + /** + * Moves the node at the given index down to its proper place in the heap. + * @param index The index of the node to move down. + */ + siftDown(index) { + /** @returns the index containing the smaller value */ + const minIndex = (left, right) => { + if (right >= this.data.length) { + if (left >= this.data.length) { + return -1; + } else { + return left; + } + } else { + if (this.compareFn(this.data[left], this.data[right]) <= 0) { + return left; + } else { + return right; + } + } + } + + let min = minIndex(this.left(index), this.right(index)); + + while ( + min >= 0 + && this.compareFn(this.data[index], this.data[min]) > 0 + ) { + [this.data[min], this.data[index]] = [this.data[index], this.data[min]]; + index = min; + min = minIndex(this.left(index), + this.right(index)); + } + } + + /** + * Returns the number of elements in the heap in O(1) + */ + size() { + return this.data.length; + } + + /** + * Retrieves but does not remove the root element of this heap in O(1) + * - Returns undefined if the heap is empty. + */ + peek() { + if (this.data.length > 0) { + return this.data[0]; + } else { + return undefined; + } + } +} diff --git a/javascript/355-design-twitter.js b/javascript/355-design-twitter.js new file mode 100644 index 000000000..4cb86c230 --- /dev/null +++ b/javascript/355-design-twitter.js @@ -0,0 +1,228 @@ +/* + * https://leetcode.com/problems/design-twitter/ + * https://www.youtube.com/watch?v=pNichitDD2E&t=779 + * Using Heap Datastructure (MaxHeap) +*/ + +/** + * type IUserTweetTimeStamp = { + * time: number, + * tweetId: number + * }; + * + * type IUserTweetTracker = { + * user: IUserTweetTimeStamp, + * followeeId: number, + * index: number + * }; + */ + + class Twitter { + constructor() { + this.followMap = new Map(); + this.tweetMap = new Map(); + this.time = 0; + } + + postTweet (userId, tweetId) { + if (!this.tweetMap.has(userId)) { + this.tweetMap.set(userId, []); + } + this.tweetMap.get(userId).push({ time: this.time, tweetId }); + this.time += 1; + } + + getNewsFeed (userId) { + const result = []; + // a,b: IUserTweetTracker + const maxHeap = new Heap((a, b) => b.user.time - a.user.time); + // getordefault + if (!this.followMap.has(userId)) { + this.followMap.set(userId, new Set()); + } + const list = this.followMap.get(userId); + list.add(userId); + + // iterate through the set + for (let id of Array.from(this.followMap.get(userId))) { + if (this.tweetMap.has(id)) { + const index = this.tweetMap.get(id).length - 1; + if (index >= 0) { + const current = this.tweetMap.get(id)[index]; + maxHeap.add({ user: current, followeeId: userId, index: index - 1 }); + } + } + } + + // check heap + while (maxHeap.size() > 0 && result.length < 10) { + const ustt = maxHeap.remove(); + result.push(ustt.user.tweetId); + if (ustt.index >= 0) { + const temp = this.tweetMap.get(ustt.followeeId)[ustt.index]; + maxHeap.add({ + user: temp, + followeeId: ustt.followeeId, + index: ustt.index - 1, + }); + } + } + // return list + return result; + } + + follow (followerId, followeeId) { + // getOrDefault + if (!this.followMap.has(followerId)) { + this.followMap.set(followerId, new Set()); + } + const list = this.followMap.get(followerId); + list.add(followeeId); + } + + unfollow (followerId, followeeId) { + // remove if present + if ( + this.followMap.has(followerId) && + this.followMap.get(followerId).has(followeeId) + ) { + const list = this.followMap.get(followerId); + list.delete(followeeId); + } + } + } + +/** + * egghead.io - Heap + * https://github.com/basarat/algorithms/blob/master/src/heap/heap.ts + */ + +/** + * Implements the heap data structure + * A heap is used as a priority queue + * Note: The default compare behavior gives you a min heap + * @constructor ((a,b) => void) comparator, by default - ascending (a-b) + */ +class Heap { + + + constructor(compareFn = undefined) { + this.data = []; + this.compareFn = compareFn && typeof compareFn === 'function' ? compareFn : (a, b) => (a - b); + } + + left(nodeIndex) { + return (2 * nodeIndex) + 1; + } + + right(nodeIndex) { + return (2 * nodeIndex) + 2; + } + + parent(nodeIndex) { + return nodeIndex % 2 == 0 + ? (nodeIndex - 2) / 2 + : (nodeIndex - 1) / 2; + } + + /** + * Adds the given element into the heap in O(logn) + */ + add(element) { + this.data.push(element); + this.siftUp(this.data.length - 1); + } + + /** + * Moves the nod at the given index up to its proper place in the heap. + * @param index The index of the node to move up. + */ + siftUp(index) { + let parent = this.parent(index); + while (index > 0 && this.compareFn(this.data[parent], this.data[index]) > 0) { + [this.data[parent], this.data[index]] = [this.data[index], this.data[parent]]; + index = parent; + parent = this.parent(index); + } + } + + /** + * Retrieves and removes the root element of this heap in O(logn) + * - Returns undefined if the heap is empty. + */ + remove() { + if (this.data.length > 0) { + const root = this.data[0]; + const last = this.data.pop(); + if (this.data.length > 0) { + this.data[0] = last; + this.siftDown(0); + } + return root; + } else { + return undefined; + } + } + + /** + * Moves the node at the given index down to its proper place in the heap. + * @param index The index of the node to move down. + */ + siftDown(index) { + /** @returns the index containing the smaller value */ + const minIndex = (left, right) => { + if (right >= this.data.length) { + if (left >= this.data.length) { + return -1; + } else { + return left; + } + } else { + if (this.compareFn(this.data[left], this.data[right]) <= 0) { + return left; + } else { + return right; + } + } + } + + let min = minIndex(this.left(index), this.right(index)); + + while ( + min >= 0 + && this.compareFn(this.data[index], this.data[min]) > 0 + ) { + [this.data[min], this.data[index]] = [this.data[index], this.data[min]]; + index = min; + min = minIndex(this.left(index), + this.right(index)); + } + } + + /** + * Returns the number of elements in the heap in O(1) + */ + size() { + return this.data.length; + } + + /** + * Retrieves but does not remove the root element of this heap in O(1) + * - Returns undefined if the heap is empty. + */ + peek() { + if (this.data.length > 0) { + return this.data[0]; + } else { + return undefined; + } + } +} + +// TESTING +const twitterMy = new Twitter(); +twitterMy.postTweet(1, 5); +console.log("News feed: ", twitterMy.getNewsFeed(1)); +twitterMy.follow(1, 2); +twitterMy.postTweet(2, 6); +console.log("News feed: ", twitterMy.getNewsFeed(1)); diff --git a/javascript/621-task-scheduler.js b/javascript/621-task-scheduler.js new file mode 100644 index 000000000..52d1cffc5 --- /dev/null +++ b/javascript/621-task-scheduler.js @@ -0,0 +1,182 @@ +/** + * Task Scheduler + * https://www.youtube.com/watch?v=s8p8ukTyA2I + * Leetcode #621 - https://leetcode.com/problems/task-scheduler/ + * Space time complexity + * Time: O(n*logk) + * Space: O(n) + * @param {string []} tasks + * @param {number} k + * @returns {number} interval +*/ +const leastInterval = (tasks, k) => { + // edge case + if (k < 1) return tasks.length; + // set interval count + let interval = 0; + + // add frequency map - O(n) + const map = new Map(); + for (let t of tasks) { + if (!map.has(t)) map.set(t, 0); + const count = map.get(t); + map.set(t, count + 1); + } + + // create a maxHeap + const maxHeap = new Heap((a, b) => b - a); + // prefill heap - O(n) + Array.from(map.values()).map((item) => maxHeap.add(item)); + + // time complexity - O(n*logn) + while (maxHeap.size() > 0) { + const waitList = []; + let n = k + 1; + while (n > 0 && maxHeap.size() > 0) { + interval += 1; + let entry = maxHeap.remove(); + if (entry > 1) { + entry = entry - 1; + waitList.push(entry); + } + n -= 1; + } + waitList.map((item) => maxHeap.add(item)); + if (maxHeap.size() > 0) { + interval += n; + } + } + return interval; +}; + +/** + * egghead.io - Heap + * https://github.com/basarat/algorithms/blob/master/src/heap/heap.ts + */ + +/** + * Implements the heap data structure + * A heap is used as a priority queue + * Note: The default compare behavior gives you a min heap + * @constructor ((a,b) => void) comparator, by default - ascending (a-b) + */ +class Heap { + + + constructor(compareFn = undefined) { + this.data = []; + this.compareFn = compareFn && typeof compareFn === 'function' ? compareFn : (a, b) => (a - b); + } + + left(nodeIndex) { + return (2 * nodeIndex) + 1; + } + + right(nodeIndex) { + return (2 * nodeIndex) + 2; + } + + parent(nodeIndex) { + return nodeIndex % 2 == 0 + ? (nodeIndex - 2) / 2 + : (nodeIndex - 1) / 2; + } + + /** + * Adds the given element into the heap in O(logn) + */ + add(element) { + this.data.push(element); + this.siftUp(this.data.length - 1); + } + + /** + * Moves the nod at the given index up to its proper place in the heap. + * @param index The index of the node to move up. + */ + siftUp(index) { + let parent = this.parent(index); + while (index > 0 && this.compareFn(this.data[parent], this.data[index]) > 0) { + [this.data[parent], this.data[index]] = [this.data[index], this.data[parent]]; + index = parent; + parent = this.parent(index); + } + } + + /** + * Retrieves and removes the root element of this heap in O(logn) + * - Returns undefined if the heap is empty. + */ + remove() { + if (this.data.length > 0) { + const root = this.data[0]; + const last = this.data.pop(); + if (this.data.length > 0) { + this.data[0] = last; + this.siftDown(0); + } + return root; + } else { + return undefined; + } + } + + /** + * Moves the node at the given index down to its proper place in the heap. + * @param index The index of the node to move down. + */ + siftDown(index) { + /** @returns the index containing the smaller value */ + const minIndex = (left, right) => { + if (right >= this.data.length) { + if (left >= this.data.length) { + return -1; + } else { + return left; + } + } else { + if (this.compareFn(this.data[left], this.data[right]) <= 0) { + return left; + } else { + return right; + } + } + } + + let min = minIndex(this.left(index), this.right(index)); + + while ( + min >= 0 + && this.compareFn(this.data[index], this.data[min]) > 0 + ) { + [this.data[min], this.data[index]] = [this.data[index], this.data[min]]; + index = min; + min = minIndex(this.left(index), + this.right(index)); + } + } + + /** + * Returns the number of elements in the heap in O(1) + */ + size() { + return this.data.length; + } + + /** + * Retrieves but does not remove the root element of this heap in O(1) + * - Returns undefined if the heap is empty. + */ + peek() { + if (this.data.length > 0) { + return this.data[0]; + } else { + return undefined; + } + } +} + +// TESTING +const task01 = ["A", "A", "A", "B", "B", "B"]; +const n = 2; +console.log("Least idle interval is: ", leastInterval(task01, n)); // 8 diff --git a/javascript/973-k-closest-points-to-origin.js b/javascript/973-k-closest-points-to-origin.js new file mode 100644 index 000000000..6aa1188b1 --- /dev/null +++ b/javascript/973-k-closest-points-to-origin.js @@ -0,0 +1,194 @@ +/** + * https://leetcode.com/problems/k-closest-points-to-origin/ + * https://www.youtube.com/watch?v=rI2EBUEMfTk + * @param {number[][]} points + * @param {number} k + * @return {number[][]} + */ +const kClosest = (points, k) => { + // create a maxHeap + const distHeap = new Heap( + (a, b) => b[0] * b[0] + b[1] * b[1] - (a[0] * a[0] + a[1] * a[1]) + ); + // Add first k points to heap + // // O (k * logK) time + for (let i = 0; i < k; i += 1) { + distHeap.add(points[i]); + } + + // compare remaining elements + // O(n-k) * logK) time + for (let i = k; i < points.length; i += 1) { + const currentPoint = points[i]; + const currentDist = + currentPoint[0] * currentPoint[0] + currentPoint[1] * currentPoint[1]; + const peekElement = distHeap.peek(); + const heapPointDist = + peekElement[0] * peekElement[0] + peekElement[1] * peekElement[1]; + if (currentDist < heapPointDist) { + distHeap.remove(); + distHeap.add(currentPoint); + } + } + + // next we create an array to contain points up to K + const results = []; + for (let i = 0; i < k; i += 1) { + results[i] = distHeap.remove(); + } + return results; +}; + +/** + * egghead.io - Heap + * https://github.com/basarat/algorithms/blob/master/src/heap/heap.ts + */ + +/** + * Implements the heap data structure + * A heap is used as a priority queue + * Note: The default compare behavior gives you a min heap + * @constructor ((a,b) => void) comparator, by default - ascending (a-b) + */ +class Heap { + + + constructor(compareFn = undefined) { + this.data = []; + this.compareFn = compareFn && typeof compareFn === 'function' ? compareFn : (a, b) => (a - b); + } + + left(nodeIndex) { + return (2 * nodeIndex) + 1; + } + + right(nodeIndex) { + return (2 * nodeIndex) + 2; + } + + parent(nodeIndex) { + return nodeIndex % 2 == 0 + ? (nodeIndex - 2) / 2 + : (nodeIndex - 1) / 2; + } + + /** + * Adds the given element into the heap in O(logn) + */ + add(element) { + this.data.push(element); + this.siftUp(this.data.length - 1); + } + + /** + * Moves the nod at the given index up to its proper place in the heap. + * @param index The index of the node to move up. + */ + siftUp(index) { + let parent = this.parent(index); + while (index > 0 && this.compareFn(this.data[parent], this.data[index]) > 0) { + [this.data[parent], this.data[index]] = [this.data[index], this.data[parent]]; + index = parent; + parent = this.parent(index); + } + } + + /** + * Retrieves and removes the root element of this heap in O(logn) + * - Returns undefined if the heap is empty. + */ + remove() { + if (this.data.length > 0) { + const root = this.data[0]; + const last = this.data.pop(); + if (this.data.length > 0) { + this.data[0] = last; + this.siftDown(0); + } + return root; + } else { + return undefined; + } + } + + /** + * Moves the node at the given index down to its proper place in the heap. + * @param index The index of the node to move down. + */ + siftDown(index) { + /** @returns the index containing the smaller value */ + const minIndex = (left, right) => { + if (right >= this.data.length) { + if (left >= this.data.length) { + return -1; + } else { + return left; + } + } else { + if (this.compareFn(this.data[left], this.data[right]) <= 0) { + return left; + } else { + return right; + } + } + } + + let min = minIndex(this.left(index), this.right(index)); + + while ( + min >= 0 + && this.compareFn(this.data[index], this.data[min]) > 0 + ) { + [this.data[min], this.data[index]] = [this.data[index], this.data[min]]; + index = min; + min = minIndex(this.left(index), + this.right(index)); + } + } + + /** + * Returns the number of elements in the heap in O(1) + */ + size() { + return this.data.length; + } + + /** + * Retrieves but does not remove the root element of this heap in O(1) + * - Returns undefined if the heap is empty. + */ + peek() { + if (this.data.length > 0) { + return this.data[0]; + } else { + return undefined; + } + } +} + + + +// TESTING +const origins01 = [ + [1, 3], + [-2, 2], +]; +const k01 = 1; +const origins02 = [ + [3, 3], + [5, -1], + [-2, 4], +]; +const k02 = 2; + +console.log("K closest points to origin: ", kClosest(origins01, k01)); // [[-2,2]] +console.log("K closest points to origin: ", kClosest(origins02, k02)); // [[-2,4],[3,3]] + +/* + * NOTES + * Time complexity - O(nlogk). + * Make a MAX HEAP of size k rather than a min-heap. + * Then, when an element 'x' comes in, just compare + * if 'x' is smaller than the largest element in the max heap( the top one.) or not. + * If x is smaller, We can drop the previous top element away and add 'x' to our max heap. + */