From 7ec28acbcda41405e898459c244d1f6602475394 Mon Sep 17 00:00:00 2001 From: Neha Goud Date: Sun, 22 Feb 2026 15:44:45 +0530 Subject: [PATCH 1/3] feat: add Weighted Interval Scheduling using DP and Binary Search --- .../WeightedIntervalScheduling.js | 67 +++++++++++++++++++ .../tests/WeightedIntervalScheduling.test.js | 24 +++++++ 2 files changed, 91 insertions(+) create mode 100644 Dynamic-Programming/WeightedIntervalScheduling.js create mode 100644 Dynamic-Programming/tests/WeightedIntervalScheduling.test.js diff --git a/Dynamic-Programming/WeightedIntervalScheduling.js b/Dynamic-Programming/WeightedIntervalScheduling.js new file mode 100644 index 0000000000..557f5cbde3 --- /dev/null +++ b/Dynamic-Programming/WeightedIntervalScheduling.js @@ -0,0 +1,67 @@ +/** + * Solves the Weighted Interval Scheduling problem. + * + * Given intervals with start time, end time, and profit, + * returns the maximum profit such that no intervals overlap. + * + * Uses Dynamic Programming + Binary Search. + * + * Time Complexity: O(n log n) + * Space Complexity: O(n) + * + * @param {{start:number, end:number, profit:number}[]} intervals + * @returns {number} + */ + +const weightedIntervalScheduling = (intervals) => { + if (!Array.isArray(intervals)) { + throw new Error('Input must be an array of intervals') + } + + if (intervals.length === 0) return 0 + + // Sort intervals by end time + intervals.sort((a, b) => a.end - b.end) + + const n = intervals.length + const dp = Array(n).fill(0) + + dp[0] = intervals[0].profit + + // Binary search to find last non-overlapping interval + const findLastNonOverlapping = (index) => { + let left = 0 + let right = index - 1 + + while (left <= right) { + const mid = Math.floor((left + right) / 2) + + if (intervals[mid].end <= intervals[index].start) { + if (intervals[mid + 1]?.end <= intervals[index].start) { + left = mid + 1 + } else { + return mid + } + } else { + right = mid - 1 + } + } + + return -1 + } + + for (let i = 1; i < n; i++) { + let includeProfit = intervals[i].profit + const lastIndex = findLastNonOverlapping(i) + + if (lastIndex !== -1) { + includeProfit += dp[lastIndex] + } + + dp[i] = Math.max(dp[i - 1], includeProfit) + } + + return dp[n - 1] +} + +export { weightedIntervalScheduling } \ No newline at end of file diff --git a/Dynamic-Programming/tests/WeightedIntervalScheduling.test.js b/Dynamic-Programming/tests/WeightedIntervalScheduling.test.js new file mode 100644 index 0000000000..815482f806 --- /dev/null +++ b/Dynamic-Programming/tests/WeightedIntervalScheduling.test.js @@ -0,0 +1,24 @@ +import { weightedIntervalScheduling } from '../WeightedIntervalScheduling' + +test('basic weighted interval scheduling', () => { + const intervals = [ + { start: 1, end: 3, profit: 5 }, + { start: 2, end: 5, profit: 6 }, + { start: 4, end: 6, profit: 5 }, + { start: 6, end: 7, profit: 4 }, + { start: 5, end: 8, profit: 11 }, + { start: 7, end: 9, profit: 2 } + ] + + expect(weightedIntervalScheduling(intervals)).toBe(17) +}) + +test('empty intervals', () => { + expect(weightedIntervalScheduling([])).toBe(0) +}) + +test('single interval', () => { + expect( + weightedIntervalScheduling([{ start: 1, end: 2, profit: 10 }]) + ).toBe(10) +}) \ No newline at end of file From a99928f82c6f6c434c83dceff5e2a5a3c1510e30 Mon Sep 17 00:00:00 2001 From: Neha Goud Date: Sun, 22 Feb 2026 20:02:18 +0530 Subject: [PATCH 2/3] style: fix prettier formatting --- Dynamic-Programming/WeightedIntervalScheduling.js | 2 +- .../tests/WeightedIntervalScheduling.test.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Dynamic-Programming/WeightedIntervalScheduling.js b/Dynamic-Programming/WeightedIntervalScheduling.js index 557f5cbde3..59571c2bab 100644 --- a/Dynamic-Programming/WeightedIntervalScheduling.js +++ b/Dynamic-Programming/WeightedIntervalScheduling.js @@ -64,4 +64,4 @@ const weightedIntervalScheduling = (intervals) => { return dp[n - 1] } -export { weightedIntervalScheduling } \ No newline at end of file +export { weightedIntervalScheduling } diff --git a/Dynamic-Programming/tests/WeightedIntervalScheduling.test.js b/Dynamic-Programming/tests/WeightedIntervalScheduling.test.js index 815482f806..b80b8c18ea 100644 --- a/Dynamic-Programming/tests/WeightedIntervalScheduling.test.js +++ b/Dynamic-Programming/tests/WeightedIntervalScheduling.test.js @@ -18,7 +18,7 @@ test('empty intervals', () => { }) test('single interval', () => { - expect( - weightedIntervalScheduling([{ start: 1, end: 2, profit: 10 }]) - ).toBe(10) -}) \ No newline at end of file + expect(weightedIntervalScheduling([{ start: 1, end: 2, profit: 10 }])).toBe( + 10 + ) +}) From 65195321167a6d62eaf45529146bd8e8bdbfb452 Mon Sep 17 00:00:00 2001 From: Neha Goud Date: Mon, 23 Feb 2026 19:31:54 +0530 Subject: [PATCH 3/3] style: fix prettier formatting --- Dynamic-Programming/WeightedIntervalScheduling.js | 7 ++++--- .../tests/WeightedIntervalScheduling.test.js | 6 ++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Dynamic-Programming/WeightedIntervalScheduling.js b/Dynamic-Programming/WeightedIntervalScheduling.js index 59571c2bab..39f1624384 100644 --- a/Dynamic-Programming/WeightedIntervalScheduling.js +++ b/Dynamic-Programming/WeightedIntervalScheduling.js @@ -20,7 +20,6 @@ const weightedIntervalScheduling = (intervals) => { if (intervals.length === 0) return 0 - // Sort intervals by end time intervals.sort((a, b) => a.end - b.end) const n = intervals.length @@ -28,7 +27,6 @@ const weightedIntervalScheduling = (intervals) => { dp[0] = intervals[0].profit - // Binary search to find last non-overlapping interval const findLastNonOverlapping = (index) => { let left = 0 let right = index - 1 @@ -37,7 +35,10 @@ const weightedIntervalScheduling = (intervals) => { const mid = Math.floor((left + right) / 2) if (intervals[mid].end <= intervals[index].start) { - if (intervals[mid + 1]?.end <= intervals[index].start) { + if ( + mid + 1 <= right && + intervals[mid + 1].end <= intervals[index].start + ) { left = mid + 1 } else { return mid diff --git a/Dynamic-Programming/tests/WeightedIntervalScheduling.test.js b/Dynamic-Programming/tests/WeightedIntervalScheduling.test.js index b80b8c18ea..4260522a53 100644 --- a/Dynamic-Programming/tests/WeightedIntervalScheduling.test.js +++ b/Dynamic-Programming/tests/WeightedIntervalScheduling.test.js @@ -22,3 +22,9 @@ test('single interval', () => { 10 ) }) + +test('throws error for invalid input', () => { + expect(() => weightedIntervalScheduling(null)).toThrow( + 'Input must be an array of intervals' + ) +})