Jaconir
Skip to lesson
medium
25 minIntern through senior

When to use. Sorted choice + a clear invariant (earliest end, jump reach, Huffman-style merge).

Complexity. Time Usually O(n log n) from the sort, then O(n). Space O(1) extra if you sort in place; else O(n).

Who gets asked. Interval scheduling is intern/mid. Proving greediness vs falling back to DP is the senior bar.

Prereq. Sorting; a sense of DP so you know the alternative.

Greedy Algorithms

Learn the intuitive approach of making the best local choice at each step, and understand when this simple strategy leads to a global optimum.

What Is a Greedy Algorithm?

A greedy algorithm builds up a solution piece by piece, always choosing the next piece that offers the most obvious and immediate benefit. Think about giving change for 41 cents using standard US coins (25, 10, 5, 1). The greedy approach is what a human does naturally: take the biggest coin first (a quarter), then the biggest coin for the remainder (a dime), and so on. In this case, 25 + 10 + 5 + 1 works.

This strategy is appealing because it's simple and fast. However, the catch is that it doesn't always work. The greedy choice property—where a locally optimal choice leads to a globally optimal solution—only holds for certain types of problems. A key part of mastering greedy algorithms is learning to identify these problems and knowing when a more complex approach like Dynamic Programming is necessary. Interval scheduling (earliest finish) is intern/mid. The senior bar is proving the invariant or switching to DP when greed fails (classic coin systems that are not canonical).

Problem: Coin Change
The greedy approach for making change is simple: always pick the largest coin that is less than or equal to the remaining amount. This works for standard coin systems (like USD) but can fail for others.
[25,10,5,1]
1function greedyCoinChange(coins, amount) {
2 coins.sort((a, b) => b - a); // Largest first
3 let count = 0;
4 let result = [];
5
6 for (const coin of coins) {
7 while (amount >= coin) {
8 amount -= coin;
9 result.push(coin);
10 }
11 }
12
13 if (amount === 0) {
14 return result; // Optimal
15 }
16 return null; // Failed to make change
17}
Problem: Fractional Knapsack
You have a knapsack that can hold a certain weight, and a set of items, each with a weight and a value. You can take fractions of items. The greedy strategy is to always take as much as possible of the item with the highest value-to-weight ratio.

Item A

W: 10, V: 60

Ratio: 6

Item B

W: 20, V: 100

Ratio: 5

Item C

W: 30, V: 120

Ratio: 4
Total Value: $0.00 | Remaining Capacity: 50kg
Start with an empty knapsack. Capacity: 50kg.
1function fractionalKnapsack(capacity, items) {
2 // 1. Calculate value/weight ratio and sort
3 items.forEach(item => item.ratio = item.value / item.weight);
4 items.sort((a, b) => b.ratio - a.ratio);
5
6 let totalValue = 0;
7 let currentWeight = 0;
8
9 for (const item of items) {
10 if (currentWeight + item.weight <= capacity) {
11 // 2. Take the whole item
12 currentWeight += item.weight;
13 totalValue += item.value;
14 } else {
15 // 3. Take a fraction of the item
16 const remainingWeight = capacity - currentWeight;
17 totalValue += item.ratio * remainingWeight;
18 currentWeight += remainingWeight;
19 break; // Knapsack is full
20 }
21 }
22 return totalValue;
23}
Problem: Activity Selection
Given a set of activities with start and end times, find the maximum number of non-overlapping activities. The greedy choice is to always select the activity that finishes earliest.
ID: 1(1-4)
ID: 2(3-5)
ID: 3(0-6)
ID: 4(5-7)
ID: 5(3-9)
ID: 6(5-9)
ID: 7(6-10)
ID: 8(8-11)
ID: 9(8-12)
ID: 10(2-14)
ID: 11(12-16)
Start. Sort activities by their finish times.
1function activitySelection(activities) {
2 // 1. Sort activities by finish time
3 activities.sort((a, b) => a.end - b.end);
4
5 const result = [];
6 if (activities.length === 0) return result;
7
8 // 2. Select the first activity
9 result.push(activities[0]);
10 let lastFinishTime = activities[0].end;
11
12 for (let i = 1; i < activities.length; i++) {
13 const currentActivity = activities[i];
14
15 // 3. If current activity starts after the last one finished...
16 if (currentActivity.start >= lastFinishTime) {
17 // ...select it and update the last finish time.
18 result.push(currentActivity);
19 lastFinishTime = currentActivity.end;
20 }
21 }
22 return result;
23}
Coding Challenge
nums[i] is the farthest you may jump from i. Start at 0. Can you reach the last index? Track the farthest reachable. O(n).
J
Dafin Edison J
Creator & Developer of Jaconir

I build things for the web. From immersive games to practical tools, my goal is to create products that people love to use and to share the knowledge I've gained along the way.

Saved in this browser. No account.