Activity selection / N meetings in one room / maximum trainsEasy1 approach
Problem
Given activities (or meetings) with start and end times, select as many as possible that do not overlap, for a single person or room. Maximum Trains applies the same idea separately to each platform.
Example 1
Input: start = [1, 3, 0, 5, 8, 5], end = [2, 4, 6, 7, 9, 9] Output: 4
Meetings 1, 2, 4, 5 (by 1-based index).
Also asked as: Activity Selection Problem · Find maximum meetings in one room · Maximum trains for which stoppage can be provided
1. Sort by finish time, greedily take non-overlapping
Time O(n log n)Space O(1)
Always pick the activity that finishes earliest and does not clash with the last chosen one — it leaves the most room for the rest.
function maxActivities(activities) { // [start, end] activities.sort((a, b) => a[1] - b[1]); let count = 0, lastEnd = -Infinity; for (const [s, e] of activities) { if (s >= lastEnd) { count++; lastEnd = e; } } return count;}Job sequencing with deadlines (maximise profit)Medium1 approach
Problem
Each job takes one unit of time and has a deadline and a profit. Schedule jobs, one at a time, so that each finishes by its deadline and the total profit is maximised. Return the number of jobs done and the profit.
Example 1
Input: jobs (deadline, profit) = [(4,20), (1,10), (1,40), (1,30)] Output: 2 jobs, profit 60
Also asked as: Job SequencingProblem · Weighted Job Scheduling
1. Sort by profit desc, place each job in the latest free slot ≤ its deadline
Time O(n log n + n·maxDeadline)Space O(maxDeadline)
High-profit jobs first; give each the last available day before its deadline (a DSU of "next free slot" makes slot-finding near O(1)).
function jobSequencing(jobs) { // {id, deadline, profit} jobs.sort((a, b) => b.profit - a.profit); const maxD = Math.max(...jobs.map((j) => j.deadline)); const slot = new Array(maxD + 1).fill(null); let profit = 0, count = 0; for (const j of jobs) { for (let d = j.deadline; d >= 1; d--) { if (!slot[d]) { slot[d] = j.id; profit += j.profit; count++; break; } } } return { count, profit };}Weighted Job Scheduling where jobs may overlap and you want max total profit is DP: sort by end time, dp[i] = max(dp[i-1], profit[i] + dp[last non-conflicting]).
Huffman codingMedium1 approach
Problem
Given characters and their frequencies, build an optimal prefix-free binary code: frequent characters get shorter codes, and no code is a prefix of another. Print each character's code, for example in preorder of the Huffman tree.
Example 1
Input: chars = "abcdef", freq = [5, 9, 12, 13, 16, 45] Output: f: 0, c: 100, d: 101, a: 1100, b: 1101, e: 111
Also asked as: Huffman Coding
1. Min-heap merge the two least-frequent nodes
Time O(n log n)Space O(n)
Repeatedly combine the two lowest-frequency subtrees into a parent whose frequency is their sum. The resulting tree gives prefix codes; left = 0, right = 1.
function huffman(freq) { // freq: { char: count } let heap = Object.entries(freq).map(([ch, f]) => ({ ch, f, left: null, right: null })); heap.sort((a, b) => a.f - b.f); while (heap.length > 1) { const a = heap.shift(), b = heap.shift(); const node = { ch: null, f: a.f + b.f, left: a, right: b }; let i = heap.findIndex((x) => x.f > node.f); i === -1 ? heap.push(node) : heap.splice(i, 0, node); } const codes = {}; const walk = (n, code) => { if (!n) return; if (n.ch !== null) { codes[n.ch] = code || '0'; return; } walk(n.left, code + '0'); walk(n.right, code + '1'); }; walk(heap[0], ''); return codes;}Fractional knapsackEasy1 approach
Problem
Items have values and weights, and the knapsack has capacity W. You may take any fraction of an item. Return the maximum total value you can carry.
Example 1
Input: values = [60, 100, 120], weights = [10, 20, 30], W = 50 Output: 240
Take items 1 and 2 whole and 2/3 of item 3.
Also asked as: Fractional Knapsack Problem
1. Sort by value/weight ratio, take greedily, split the last item
Time O(n log n)Space O(1)
Unlike 0/1 knapsack, you can take fractions, so always take from the item with the best value density.
function fractionalKnapsack(items, capacity) { // items: [value, weight] items.sort((a, b) => b[0] / b[1] - a[0] / a[1]); let total = 0; for (const [v, w] of items) { if (capacity >= w) { capacity -= w; total += v; } else { total += v * (capacity / w); break; } } return total;}Minimum number of coins / minimum cost of ropes / connect n ropesEasy1 approach
Problem
(1) Minimum coins: using Indian currency denominations (1, 2, 5, 10, 20, 50, 100, 500, 2000), make change for V with the fewest notes and coins — greedy works for this canonical system. (2) Connect ropes: joining two ropes costs the sum of their lengths; minimise the total cost of joining them all.
Example 1
Input: V = 70 Output: [50, 20]
Example 2
Input: ropes [4, 3, 2, 6] Output: 29
Also asked as: Greedy Algorithm to find Minimum number of Coins · Minimum Cost of ropes
1. Coins: take the largest denomination that fits, repeatedly
Time O(amount / minCoin) or O(coins·log)Space O(1)
For canonical coin systems (1, 2, 5, 10, …), greedily using the biggest coin ≤ the remaining amount is optimal. (For arbitrary systems use DP — see "Coin Change".)
function minCoins(amount, coins = [1, 2, 5, 10, 20, 50, 100, 500, 2000]) { coins.sort((a, b) => b - a); const used = []; for (const c of coins) while (amount >= c) { amount -= c; used.push(c); } return used;}Connect n ropes with minimum cost: min-heap, always join the two shortest ropes and add the combined length to the cost (Huffman-style) — see the Heap section.
Minimum number of platformsMedium1 approach
Problem
Given the arrival and departure times of trains at a station, return the minimum number of platforms needed so that no train has to wait.
Example 1
Input: arr = [900, 940, 950, 1100, 1500, 1800], dep = [910, 1200, 1120, 1130, 1900, 2000] Output: 3
Also asked as: Minimum Platforms Problem
1. Sort arrivals and departures separately, sweep
Time O(n log n)Space O(1)
Merge the two sorted time streams; +1 platform on an arrival, −1 on a departure. The running maximum is the answer.
function minPlatforms(arr, dep) { arr.sort((a, b) => a - b); dep.sort((a, b) => a - b); let platforms = 0, best = 0, i = 0, j = 0; while (i < arr.length) { if (arr[i] <= dep[j]) { platforms++; i++; best = Math.max(best, platforms); } else { platforms--; j++; } } return best;}Array-value greedy tricks — k negations, arr[i]*i, abs-diff sum, three-stack equal sumMedium2 approaches
Problem
Four short greedy problems:
Maximise the array sum after exactly k negations (you may negate the same element more than once). Maximise Σ arr[i]·i by rearranging the array. Arrange the array to maximise the sum of absolute differences between neighbours, treated as a circle. Remove top elements from three stacks until their sums are equal and as large as possible.
Example 1
Input: k negations: [-2, 0, 5, -1, 2], k = 4 Output: 10
Example 2
Input: arr[i]*i: [3, 5, 6, 1] Output: 31
Sorted [1, 3, 5, 6]: 0 + 3 + 10 + 18.
Also asked as: Maximize array sum after K negations · Maximize the sum of arr[i]*i · Maximum sum of absolute difference of an array · Maximize sum of consecutive differences in a circular array · Find maximum sum possible equal sum of three stacks · Maximum product subset of an array
1. K negations — flip the most negative each time
Time O(n log n)Space O(1)
Sort ascending; flip negatives left to right while k remains. If k is still odd afterwards, flip the current smallest absolute value once.
function maxSumAfterKNegations(a, k) { a.sort((x, y) => x - y); for (let i = 0; i < a.length && k > 0 && a[i] < 0; i++) { a[i] = -a[i]; k--; } if (k % 2 === 1) { const m = Math.min(...a); const idx = a.indexOf(m); a[idx] = -a[idx]; } return a.reduce((s, v) => s + v, 0);}2. Maximise Σ arr[i]·i — sort ascending
Time O(n log n)Space O(1)
Larger values deserve larger indices, so sorting ascending maximises the weighted sum (rearrangement inequality).
function maxSumIndexProduct(a) { a.sort((x, y) => x - y); return a.reduce((s, v, i) => s + v * i, 0);}Max sum of |arr[i]−arr[i+1]| over an arrangement: sort, then interleave the small and large halves (zig-zag). Three stacks equal sum: prefix-sum each stack from the top, pop the largest total until all three match. Max product subset: multiply all non-zero elements; if the count of negatives is odd, divide out the largest (least-magnitude) negative.
Smallest number with N digits and digit sum SEasy1 approach
Problem
Return the smallest number that has exactly n digits (no leading zero) whose digits add up to s, or −1 if none exists.
Example 1
Input: n = 2, s = 9 Output: 18
Example 2
Input: n = 3, s = 20 Output: 299
Also asked as: Find smallest number with given number of digits and sum of digits
1. Fill from the least-significant digit with 9s
Time O(n)Space O(n)
Reserve 1 for the leading digit (no leading zero), then put as much as possible (9s) into the rightmost positions; the leftover goes to the front.
function smallestNumber(digits, sum) { if (sum === 0) return digits === 1 ? '0' : '-1'; if (sum > 9 * digits) return '-1'; const res = new Array(digits).fill(0); sum -= 1; // reserve for the leading digit for (let i = digits - 1; i > 0; i--) { if (sum > 9) { res[i] = 9; sum -= 9; } else { res[i] = sum; sum = 0; } } res[0] = sum + 1; return res.join('');}Minimum cost to cut a board / chocolate into piecesMedium1 approach
Problem
An m × n board must be cut into 1 × 1 squares. Each horizontal and vertical cut line has its own cost, and a cut's cost is multiplied by the number of pieces it passes through at that moment. Return the minimum total cost.
Example 1
Input: x (vertical) = [2, 1, 3, 1, 4], y (horizontal) = [4, 1, 2], board 6 × 4 Output: 42
Also asked as: Minimum Cost to cut a board into squares · CHOCOLA –Chocolate
1. Sort all cut costs descending; a cut’s cost is multiplied by the current number of segments on the other axis
Time O(n log n)Space O(1)
Make expensive cuts first, while the perpendicular segment count is still low. Track how many horizontal and vertical pieces exist so far.
function minCutCost(horizontal, vertical) { horizontal.sort((a, b) => b - a); vertical.sort((a, b) => b - a); let hi = 0, vi = 0, hPieces = 1, vPieces = 1, cost = 0; while (hi < horizontal.length && vi < vertical.length) { if (horizontal[hi] >= vertical[vi]) { cost += horizontal[hi++] * vPieces; hPieces++; } else { cost += vertical[vi++] * hPieces; vPieces++; } } while (hi < horizontal.length) cost += horizontal[hi++] * vPieces; while (vi < vertical.length) cost += vertical[vi++] * hPieces; return cost;}Water connection problemMedium1 approach
Problem
n houses are joined by one-way pipes (at most one pipe in and one out per house, each with a diameter). Put a tank at every house with an outgoing pipe but no incoming one, and a tap at the house where that chain ends. For each tank–tap pair, report the smallest diameter along the chain.
Example 1
Input: n = 9, pipes (from, to, d) = [(7,4,98), (5,9,72), (4,6,10), (2,8,22), (9,7,17), (3,1,66)] Output: (2,8,22), (3,1,66), (5,6,10)
Also asked as: Water Connection Problem · Water Connection Problem
1. Follow each pipe chain from a house with no incoming pipe
Time O(n + p)Space O(n)
Build in/out maps. Every house with an outgoing but no incoming pipe is a tank source; walk the chain to its end (a house with no outgoing pipe), recording the minimum pipe diameter along the way.
function waterConnection(n, pipes) { // pipes: [from, to, diameter] const out = new Map(), incoming = new Set(); for (const [a, b, d] of pipes) { out.set(a, [b, d]); incoming.add(b); } const res = []; for (const [a] of out) { if (incoming.has(a)) continue; let cur = a, minD = Infinity; while (out.has(cur)) { const [nxt, d] = out.get(cur); minD = Math.min(minD, d); cur = nxt; } res.push([a, cur, minD]); // [tank house, tap house, max deliverable diameter] } return res;}Other classic greedy — buy max stocks, candy cost, survive on island, wine trading, amplifiers, K centers, defense of a kingdomMedium1 approach
Problem
A set of short greedy puzzles:
Buy maximum stocks — on day i you may buy at most i shares at price[i] with a fixed budget. Candy store — for every candy you buy, you get up to k others free; find the minimum and maximum total cost. Survive on an island — food lasts s days and the shop is shut on Sundays. Wine trading (GERGOVIA) — houses buy or sell wine, and moving one bottle one house costs 1. Arranging amplifiers — order the numbers to maximise a tower of powers. K centres — choose k cities to minimise the largest distance to the nearest centre. Defense of a kingdom — find the largest rectangle with no tower in its row or column.
Example 1
Input: buy stocks: price = [10, 7, 19], budget = 45 Output: 4
Buy 1 at 10 (day 1) and 2 at 7 (day 2) = 24, then 1 at 19.
Also asked as: Buy Maximum Stocks if i stocks can be bought on i-th day · Find the minimum and maximum amount to buy all N candies · Check if it is possible to survive on Island · GERGOVIA -Wine trading in Gergovia · ARRANGE -Arranging Amplifiers · K Centers Problem · DEFKIN -Defense of a Kingdom · DIEHARD -DIE HARD · Picking Up Chicks · Smallest subset with sum greater than all other elements · Minimum sum of absolute difference of pairs of two arrays · Program for Shortest Job First (or SJF) CPU Scheduling
1. Common patterns
Time mostly O(n log n)Space O(n)
Buy max stocks: sort (price, day) ascending by price; on each day buy as many as the day index allows within budget. Candy cost (buy 1 free K): sort; min = sum of the cheapest ceil(n/(k+1)), max = sum of the most expensive ceil(n/(k+1)). Survive on island: feasible iff daily food ≤ what you can buy per allowed shopping day; then greedily buy the maximum on shopping days. Wine trading (Gergovia): scan the street carrying a running surplus/deficit; cost += |carry| each step. Arranging amplifiers: to maximise a tower of exponents, put the largest base at the bottom and sort the rest ascending (compare via logs). K centers: repeatedly place a center at the point farthest from all existing centers (2-approximation). Defense of a kingdom: the largest undefended rectangle = (max gap between adjacent tower columns) × (max gap between adjacent tower rows). Smallest subset with sum > rest: sort descending and take the largest elements until their sum exceeds half the total. Min sum of |a[i]−b[i]|: sort both arrays and pair them index-by-index. SJF: sort by burst time; average waiting time = running prefix of bursts.
// Wine trading in Gergovia — total transport costfunction gergovia(demands) { // + = wants to buy, - = wants to sell let carry = 0n, cost = 0n; for (const d of demands) { carry += BigInt(d); cost += carry < 0n ? -carry : carry; } return cost.toString();}
// Smallest subset whose sum exceeds the restfunction smallestSubsetOverHalf(a) { a.sort((x, y) => y - x); const total = a.reduce((s, v) => s + v, 0); let acc = 0, k = 0; for (const v of a) { acc += v; k++; if (acc > total - acc) break; } return k;}Jump GameMedium1 approach
Problem
You start at index 0. nums[i] is the maximum jump length from index i. Return true if you can reach the last index.
Example 1
Input: [2, 3, 1, 1, 4] Output: true
Example 2
Input: [3, 2, 1, 0, 4] Output: false
Every route lands on index 3, which has jump length 0.
1. Track the furthest reachable index
Time O(n)Space O(1)
If the current index is beyond the furthest reach, you are stuck; otherwise extend the reach.
function canJump(nums) { let reach = 0; for (let i = 0; i < nums.length; i++) { if (i > reach) return false; reach = Math.max(reach, i + nums[i]); } return true;}Partition LabelsMedium1 approach
Problem
Split the string into as many parts as possible so that each letter appears in at most one part. Return the sizes of the parts.
Example 1
Input: "ababcbacadefegdehijhklij" Output: [9, 7, 8]
"ababcbaca", "defegde", "hijhklij".
1. Last occurrence + extend the cut
Time O(n)Space O(26)
Record each char’s last index. Walk the string, extending the current part’s end to the last index of every char seen; cut when i reaches it.
function partitionLabels(s) { const last = {}; for (let i = 0; i < s.length; i++) last[s[i]] = i; const res = []; let start = 0, end = 0; for (let i = 0; i < s.length; i++) { end = Math.max(end, last[s[i]]); if (i === end) { res.push(end - start + 1); start = i + 1; } } return res;}Hand of StraightsMedium1 approach
Problem
Return true if the cards can be split into groups of size groupSize, where each group is groupSize consecutive values.
Example 1
Input: hand = [1, 2, 3, 6, 2, 3, 4, 7, 8], groupSize = 3 Output: true
[1,2,3], [2,3,4], [6,7,8].
Example 2
Input: hand = [1, 2, 3, 4, 5], groupSize = 4 Output: false
1. Smallest card starts a group
Time O(n log n)Space O(n)
The smallest remaining card must begin a run. Take it and the next groupSize − 1 values from the counts; fail if any is missing.
function isNStraightHand(hand, size) { if (hand.length % size) return false; const cnt = new Map(); for (const x of hand) cnt.set(x, (cnt.get(x) ?? 0) + 1); for (const x of [...cnt.keys()].sort((a, b) => a - b)) { const need = cnt.get(x); if (!need) continue; for (let v = x; v < x + size; v++) { if ((cnt.get(v) ?? 0) < need) return false; cnt.set(v, cnt.get(v) - need); } } return true;}CandyHard1 approach
Problem
Children stand in a line with ratings. Every child gets at least one candy, and a child with a higher rating than a neighbour must get more candies than that neighbour. Return the minimum total number of candies.
Example 1
Input: [1, 0, 2] Output: 5
2, 1, 2.
Example 2
Input: [1, 2, 2] Output: 4
1, 2, 1 — equal ratings have no constraint.
1. Two passes
Time O(n)Space O(n)
Left to right: give one more than the left neighbour if rated higher. Right to left: take the max with one more than the right neighbour if rated higher. Each pass satisfies one side’s constraint without breaking the other.
function candy(ratings) { const n = ratings.length, c = new Array(n).fill(1); for (let i = 1; i < n; i++) if (ratings[i] > ratings[i - 1]) c[i] = c[i - 1] + 1; for (let i = n - 2; i >= 0; i--) if (ratings[i] > ratings[i + 1]) c[i] = Math.max(c[i], c[i + 1] + 1); return c.reduce((a, b) => a + b, 0);}Non-overlapping IntervalsMedium1 approach
Problem
Return the minimum number of intervals to remove so that the rest do not overlap. Intervals that only touch, like [1,2] and [2,3], do not overlap.
Example 1
Input: [[1,2], [2,3], [3,4], [1,3]] Output: 1
Remove [1,3].
Example 2
Input: [[1,2], [1,2], [1,2]] Output: 2
1. Sort by end, keep the earliest-finishing
Time O(n log n)Space O(1) extra
Activity selection: keeping the interval that ends first leaves the most room. Removals = total − kept.
function eraseOverlapIntervals(intervals) { intervals.sort((a, b) => a[1] - b[1]); let kept = 0, end = -Infinity; for (const [s, e] of intervals) { if (s >= end) { kept++; end = e; } } return intervals.length - kept;}Minimum Number of Arrows to Burst BalloonsMedium1 approach
Problem
Each balloon spans [xstart, xend] on the x-axis. An arrow shot straight up at x bursts every balloon with xstart ≤ x ≤ xend. Return the minimum number of arrows needed to burst them all.
Example 1
Input: [[10,16], [2,8], [1,6], [7,12]] Output: 2
Shoot at x = 6 and x = 11.
1. Sort by end, shoot at the end
Time O(n log n)Space O(1) extra
Shoot at the end of the earliest-ending balloon; it bursts every balloon starting at or before that point. Shoot again only when a balloon starts after it.
function findMinArrowShots(points) { points.sort((a, b) => a[1] - b[1]); let arrows = 0, pos = -Infinity; for (const [s, e] of points) { if (s > pos) { arrows++; pos = e; } } return arrows;}