Performance: usually O(N log N) or O(N), much faster than DP's O(N²) or higher.
Fails when a locally suboptimal choice is needed to reach the global optimum — coin change with arbitrary denominations is the classic trap.
Plain-English First
Imagine you are at a buffet with a small plate. A 'Greedy' approach means you walk down the line and take the biggest, most delicious item you see at every single station without looking ahead. You don't think, 'If I skip this pasta, I'll have room for the steak later.' You just take the best thing in front of you right now. In programming, this works perfectly for some problems (like making change with standard coins) but fails for others where a small sacrifice now leads to a massive win later.
Advantages and Disadvantages of Greedy Algorithms
Greedy algorithms are beloved for their simplicity and speed, but they come with trade-offs that every developer must understand before deploying them in production.
Advantages: - Speed: Typically O(N log N) or O(N) — far faster than DP or backtracking. - Simplicity: Easy to implement, test, and reason about. - Low memory: Often require only O(1) or O(N) extra space beyond input. - Great for real-time systems: When decisions must be made instantly with limited data, greedy heuristics are the go-to.
Disadvantages: - Not always optimal: Without the greedy choice property, the solution may be far from optimal. - Hard to verify: Proving correctness requires the exchange argument or greedy-stays-ahead — often non-trivial. - Fragile under data changes: A modification to the problem constraints (e.g., new coin denominations) can silently break greedy. - No backtracking: Greedy commits early; if a mistake is made, there is no recovery. - Difficult debugging: Suboptimal results may appear only at scale or under specific inputs, making root cause analysis challenging.
When to Use Greedy
Use greedy when the problem has proven optimal substructure and the greedy choice property. Always test with a counterexample generator before trusting it in production. When in doubt, implement a DP fallback for correctness comparison.
Production Insight
In production systems, greedy algorithms are often used as fast heuristics even when optimality isn't guaranteed. The key is to monitor the quality metric (e.g., number of jobs completed, profit) and flag deviations. A/B testing the greedy result against a slower optimal solver on sampled data can catch regressions early.
Key Takeaway
Greedy is fast and simple but not universally optimal. Always validate both the greedy choice property and optimal substructure before deployment.
Interval Scheduling — The Power of the Earliest Finish Time
The Interval Scheduling problem asks: 'Given a set of activities with start and end times, what is the maximum number of non-overlapping activities you can perform?'
Many beginners try to pick the shortest interval or the one that starts earliest, but those fail. The proven greedy choice is to always pick the activity that ends earliest. This leaves the maximum amount of time remaining for all subsequent activities.
Why does this work? Because picking the activity that finishes earliest maximizes the remaining time for the rest, and any optimal solution can be transformed step-by-step into the greedy one (exchange argument).
IntervalScheduler.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package io.thecodeforge.algorithms.greedy;
import java.util.*;
/**
* Production-grade implementation of the ActivitySelectionProblem.
* TimeComplexity: O(N log N) due to sorting.
*/
publicclassIntervalScheduler {
publicstaticclassInterval {
int start, end;
publicInterval(int start, int end) { this.start = start; this.end = end; }
@OverridepublicStringtoString() { return"(" + start + ", " + end + ")"; }
}
publicList<Interval> selectMaxActivities(List<Interval> intervals) {
if (intervals == null || intervals.isEmpty()) returnCollections.emptyList();
// Greedy Choice: Sort by finish time
intervals.sort(Comparator.comparingInt(a -> a.end));
List<Interval> result = newArrayList<>();
Interval lastSelected = intervals.get(0);
result.add(lastSelected);
for (int i = 1; i < intervals.size(); i++) {
Interval current = intervals.get(i);
// If this activity starts after or when the last one ends, pick itif (current.start >= lastSelected.end) {
result.add(current);
lastSelected = current;
}
}
return result;
}
publicstaticvoidmain(String[] args) {
IntervalScheduler scheduler = newIntervalScheduler();
List<Interval> input = Arrays.asList(
newInterval(1, 4), newInterval(3, 5), newInterval(0, 6),
newInterval(5, 7), newInterval(3, 8), newInterval(5, 9),
newInterval(6, 10), newInterval(8, 11), newInterval(12, 16)
);
System.out.println("Selected intervals: " + scheduler.selectMaxActivities(newArrayList<>(input)));
}
}
Earliest finish leaves maximum contiguous time for later activities.
Any optimal solution can be transformed to include the earliest-finishing activity without reducing count.
The proof is a classic exchange argument — swap the first activity of an optimal solution with the greedy choice.
Production Insight
Sorting by start time instead of end time is the #1 bug in production interval schedulers.
Earliest start time almost always selects fewer activities because it picks long early blockers.
Rule: if you're scheduling meetings or jobs, always sort by finish time — test with a simple counterexample first.
Key Takeaway
Earliest finish time is the only correct greedy for unweighted interval scheduling.
Start time and shortest duration are traps.
Always exchange-argue before trusting a greedy choice.
Choosing the Right Sorting Strategy for Interval Scheduling
IfGoal: maximize number of non-overlapping intervals
→
UseSort by end time (ascending). Prove via exchange argument.
IfGoal: maximize total length of scheduled time
→
UseSort by start time? Not greedy — use DP or interval graph
IfIntervals have weights (profit)
→
UseWeighted interval scheduling — DP, not greedy
IfIntervals can be preempted (split)
→
UseEarliest deadline first — sort by end time
C++ Implementation of Interval Scheduling
The same logic can be implemented in C++ using the STL. The algorithm sorts intervals by end time, then iterates to select non-overlapping activities. Time complexity remains O(N log N). The C++ version is useful when integrating with legacy systems or when performance on very large datasets requires fine-grained memory control.
interval_scheduler.cppCPP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
#include <vector>
#include <algorithm>
usingnamespace std;
structInterval {
int start, end;
Interval(int s, int e) : start(s), end(e) {}
};
vector<Interval> selectMaxActivities(vector<Interval>& intervals) {
if (intervals.empty()) return {};
// Greedy: Sort by finish timesort(intervals.begin(), intervals.end(),
[](constInterval& a, constInterval& b) { return a.end < b.end; });
vector<Interval> result;
result.push_back(intervals[0]);
int lastEnd = intervals[0].end;
for (size_t i = 1; i < intervals.size(); ++i) {
if (intervals[i].start >= lastEnd) {
result.push_back(intervals[i]);
lastEnd = intervals[i].end;
}
}
return result;
}
intmain() {
vector<Interval> intervals = {
{1, 4}, {3, 5}, {0, 6}, {5, 7},
{3, 8}, {5, 9}, {6, 10}, {8, 11}, {12, 16}
};
auto selected = selectMaxActivities(intervals);
for (auto& i : selected)
cout << "(" << i.start << ", " << i.end << ") ";
return0;
}
Output
(1, 4) (5, 7) (8, 11) (12, 16)
C++ Performance Note
The C++ sort uses introsort (O(N log N)) and the loop is O(N). The vector holds the selected intervals, which is optimal memory usage. For massive data, consider using arrays and reserving capacity upfront.
Production Insight
In C++ production schedulers, pay attention to the comparator — a wrong sort order can silently degrade throughput. Always validate with a brute-force check for small test cases before deploying.
Key Takeaway
The greedy algorithm for interval scheduling is language-agnostic. Sort by finish time, iterate, and pick non-overlapping intervals.
Coin Change — Understanding the Greedy Failure
For a 'canonical' coin system (like USD: 1, 5, 10, 25), the greedy approach of taking the largest coin first is mathematically optimal. However, if a country used coins of 1, 3, and 4 units, greedy would fail to find the best way to make 6 units. Greedy would take a 4, then two 1s (3 coins), whereas the optimal is two 3s (2 coins).
This happens because the greedy choice property breaks: taking the largest coin now prevents you from using a combination of medium coins that would be better later. The problem still has optimal substructure (DP works), but lacks the greedy choice property.
greedy_check.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- io.thecodeforge.analytics-- Tracking greedy vs optimal counts in a simulated environmentCREATETABLEdenominations (coin_val INT);
INSERTINTO denominations VALUES (1), (3), (4);
-- A simple query to show how many of the LARGEST coin greedy would take-- for an amount of 6.SELECT6AS target_amount,
MAX(coin_val) AS greedy_first_pick,
(6 / MAX(coin_val)) AS coins_taken,
(6 % MAX(coin_val)) AS remainder_to_process
FROM denominations
WHERE coin_val <= 6;
-- This logic clearly shows greedy leaves a remainder of 2, requiring 2 more coins.
A coin system is canonical if the greedy algorithm produces optimal change for all amounts. The US dollar system (1,5,10,25) is canonical. Many newer currencies (euro: 1,2,5,10,20,50) are also canonical. But arbitrary sets like (1,3,4) or (1,5,10,12,20,25) may not be. Always test with a brute-force checker before relying on greedy in production.
Production Insight
A change in coin supply (e.g., a new $12 bill) can silently break greedy optimality.
Run regression tests with known problematic amounts after any coin system update.
Rule: if the coin system changes, re-run the canonical test suite — don't assume greedy still works.
Key Takeaway
Greedy coin change works only for canonical systems.
IfCoin system may not be canonical (custom denominations)
→
UseUse dynamic programming — exact O(N * amount) is safe.
IfAmount is extremely large (billions)
→
UseGreedy may be needed for speed, but validate canonical property.
Longest Path: A Classic Greedy Failure
The longest path problem in a directed acyclic graph (DAG) asks for the path with the maximum total weight (or number of edges) from a source to a target. Greedy approaches — such as always picking the edge with the largest current weight — fail spectacularly because an early expensive edge can lead to a dead end, missing a longer overall path.
Consider a graph where from node A you can go to B (weight 10) or C (weight 9). B leads to D (weight 1) and then to target (weight 1), total = 12. C leads directly to target (weight 100), total = 109. Greedy picks B (10 > 9), then D, yielding only 12, while the optimal path (A→C→Target) gives 109. The greedy choice property does not hold because the immediate benefit of the high-weight edge blinds the algorithm to future opportunities.
This counterexample visually illustrates why greedy cannot be trusted for path-based problems unless the graph has special structure (e.g., Dijkstra works for shortest paths because edge weights are non-negative and the problem has the greedy choice property for shortest distances).
Greedy Fails on Longest Path
The longest path problem is NP-hard in general (even for DAGs it requires DP). Greedy picks the locally largest edge, but the optimal path might require a smaller initial edge that leads to a much richer subsequent route. Always use DP or topological sorting for longest path in DAGs.
Production Insight
In route optimization or resource allocation systems, never rely on greedy for longest-path variants. The cost of a wrong early decision compounds downstream. Use DP with topological ordering (O(V+E)) for DAGs, or apply A* with admissible heuristics for general graphs.
Key Takeaway
Greedy fails on longest path because it sacrifices long-term gain for short-term reward. Use DP or topological ordering for DAGs.
Huffman Coding — Greedy Data Compression
Huffman coding builds an optimal prefix-free binary code for a set of characters based on their frequencies. The greedy choice: merge the two least frequent characters at each step. This forms a binary tree where more frequent characters get shorter codes.
Why does this greedy work? Because merging the two smallest frequencies minimises the total weighted path length (the sum of frequency × depth). This is the 'optimal merge' pattern — repeatedly merging the two smallest items is a classic greedy that's provably optimal via the greedy choice property.
Greedy algorithms are fast but often wrong. You must prove correctness before relying on one in production. Two standard proof techniques exist:
Exchange Argument: Assume an optimal solution exists that differs from the greedy solution. Show you can swap elements of the optimal with greedy choices without degrading the solution, proving greedy is also optimal.
Greedy Stays Ahead: Show that at every step k, the greedy algorithm's partial solution is at least as good (or further along) as any other algorithm's partial solution. This is often used for scheduling problems.
These proofs require the problem to have optimal substructure — the optimal solution contains optimal solutions to subproblems — and the greedy choice property — a locally optimal choice is part of some globally optimal solution.
ExchangeArgumentDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package io.thecodeforge.algorithms.greedy;
/**
* Demonstration of the exchange argument for interval scheduling.
* Assume optimal solution O and greedy solution G.
* Let i be the first activity where they differ.
* O has activity j (start_j, end_j) where end_j >= end_i (greedy's choice).
* Swap j with i in O — O' is still optimal because i ends no later than j
* and doesn't overlap with earlier activities.
*/
publicclassExchangeArgumentDemo {
// No executable code — this is a conceptual proof.publicstaticvoidmain(String[] args) {
System.out.println("Proof: By induction on steps. Greedy stays ahead.");
}
}
Output
Proof: By induction on steps. Greedy stays ahead.
The Two Pillars of Greedy Correctness
Every correct greedy algorithm must satisfy two properties: (1) Optimal substructure — the problem can be broken into smaller subproblems, and the optimal solution combines optimal solutions of subproblems. (2) Greedy choice property — a globally optimal solution can be arrived at by making a locally optimal (greedy) choice.
Production Insight
Skipping correctness proofs is the root cause of countless production bugs in scheduling, networking, and optimisation systems.
Always write a counterexample generator and run it on random small instances before deploying a greedy solution.
Rule: if you can't prove it, don't trust it — brute-force verify for small N first.
Key Takeaway
Exchange argument or greedy stays ahead — pick one and prove.
Optimal substructure AND greedy choice property are both required.
Without proof, a greedy algorithm is just a heuristic with a bug waiting to happen.
Environment Setup for Algorithm Analysis
To benchmark greedy algorithms against complex DP solutions, we use a containerized environment to ensure consistent CPU/Memory constraints for timing.
DockerfileDOCKER
1
2
3
4
5
6
7
# io.thecodeforge.infrastructure
FROM eclipse-temurin:17-jdk-jammy
WORKDIR /app
COPY . /app
RUN javac io/thecodeforge/algorithms/greedy/IntervalScheduler.java
# Run with memory limits to test efficiency
CMD ["java", "-Xmx128m", "io.thecodeforge.algorithms.greedy.IntervalScheduler"]
Output
Successfully built greedy-benchmark-container
Benchmarking Greedy vs DP
When comparing greedy and DP solutions, ensure you test with identical input sizes and hardware constraints. Docker with fixed memory limits gives reproducible results. Use JMH (Java Microbenchmark Harness) for accurate microbenchmarks — System.nanoTime() is not enough.
Production Insight
Greedy algorithms are memory-efficient but can be CPU-bound if the data structure (e.g., priority queue) isn't chosen carefully.
For intervals, a simple sort + scan uses O(1) extra memory — monitor GC pressure.
Rule: profile greedy solutions with realistic data volumes before declaring victory.
Key Takeaway
Containerised benchmarking with memory limits.
Choose data structures wisely — priority queue vs sorting.
Profile before shipping.
Practice Problems to Master Greedy Algorithms
The best way to internalise when greedy works — and when it doesn't — is to solve a variety of problems. Below is a curated list of practice problems that test different greedy patterns. Work through them in order of increasing difficulty.
Fractional Knapsack (GeeksforGeeks / LeetCode) — The classic greedy success story. Sort by value-to-weight ratio and take items fully or fractionally.
Jump Game (LeetCode 55) — Determine if you can reach the last index. The greedy solution tracks the furthest reachable index.
Jump Game II (LeetCode 45) — Minimum jumps to reach the end. Greedy BFS approach.
Job Sequencing with Deadlines (GeeksforGeeks) — Maximise profit when each job has a deadline and profit. Sort by profit and slot jobs greedily.
Minimum Number of Arrows to Burst Balloons (LeetCode 452) — Interval scheduling variant. Sort by end coordinate.
Non-overlapping Intervals (LeetCode 435) — Find maximum number of non-overlapping intervals (or minimum removals). Same greedy as interval scheduling.
Assign Cookies (LeetCode 455) — Simple greedy: sort children's greed and cookie sizes, assign smallest cookie that satisfies.
Candy Distribution (LeetCode 135) — Two-pass greedy to ensure each child gets at least one candy and higher rating gets more.
Each of these problems reinforces a different aspect of the greedy choice property. Solve them and compare your greedy solution with a brute-force or DP approach for small inputs to build intuition.
How to Practice Effectively
For each problem, first write a brute-force solution (backtracking or DP) for small N to verify correctness. Then implement the greedy solution and compare. This validates the greedy choice property on the fly.
Production Insight
Many production greedy bugs come from misapplying a pattern that worked on a different problem. After practicing, create a mental checklist: 'Does my problem have the same structure as interval scheduling, coin change, or Huffman? Does the greedy choice property hold?'
Key Takeaway
Solve at least 8 different greedy problems to build pattern recognition. Always verify greedy against brute-force for small inputs until you can confidently prove correctness.
Greedy vs Dynamic Programming: A Side-by-Side Comparison
Both greedy and dynamic programming are used for optimisation problems, but they differ fundamentally in how they explore the solution space. The table below summarises the key differences.
Criterion
Greedy
Dynamic Programming
Approach
Makes a local optimal choice at each step, commits and never backtracks.
Solves subproblems recursively, stores results to avoid recomputation.
Correctness conditions
Requires both greedy choice property and optimal substructure.
Requires only optimal substructure (no greedy property needed).
Time complexity
Usually O(N log N) or O(N)
Typically O(N²) or O(N * W)
Space complexity
O(1) extra or O(N) for sorting
Often O(N * W) for tabulation
Backtracking
None
Yes — memoization or tabulation revisits subproblems.
Robustness
Fragile — a small change in problem constraints can break greedy.
Robust — handles a wide range of constraint variations.
When to choose which? If the problem has the greedy choice property, use greedy for speed. If not, or if you're unsure, use DP. In production systems, a common pattern is to implement greedy as the default fast path, but fall back to DP when greedy produces suboptimal results (detected via monitoring).
Hybrid Approach in Production
Some systems use greedy as a heuristic for large inputs and DP for small inputs where optimality is critical. This hybrid balances speed and correctness. For example, in ad allocation, greedy may handle daily budget constraints while DP optimises the last few slots.
Production Insight
When deploying a greedy solution, always implement a DP fallback in a shadow mode to compare results on a fraction of traffic. If the greedy result deviates too much from DP, alert the team. This catches silent regressions early.
Key Takeaway
Greedy is faster but fragile; DP is slower but robust. Know when to use each, and consider a hybrid strategy for production.
● Production incidentPOST-MORTEMseverity: high
The Database Scheduling That Caused Hourly Timeouts
Symptom
Payment processing jobs consistently ran past their SLA window, causing timeouts and failed transactions. Each hour, the first few jobs finished fast, but the tail jobs accumulated delays.
Assumption
The team assumed 'earliest start time' would maximise throughput because jobs would start immediately.
Root cause
Greedy by earliest start time picks jobs that start early but may end late, blocking many shorter jobs that could have completed in between. The correct greedy for throughput is earliest finish time.
Fix
Replaced the scheduling algorithm with earliest finish time greedy. Sorted jobs by end time, selected non-overlapping jobs. Throughput improved by 40% and all jobs met SLA.
Key lesson
Earliest start time is a common wrong greedy choice for interval scheduling.
Always verify the greedy choice property with a small counterexample before deployment.
In production, test greedy correctness against brute-force for small random inputs.
Production debug guideHow to detect when greedy is giving wrong results before it costs you money4 entries
Symptom · 01
Unexpectedly suboptimal results — system delivers fewer tasks, less profit, or longer routes than expected.
→
Fix
Implement a brute-force baseline for small input sizes (N ≤ 10) and compare outputs. Run it in a test environment with real data samples.
Symptom · 02
Greedy solution passes unit tests but fails integration tests with larger data.
→
Fix
Check if the problem has optimal substructure. If not, greedy cannot work. Prove or disprove the greedy choice property with a counterexample generator.
Symptom · 03
Production logs show decision quality degrades as input size grows.
→
Fix
Profile the decision rules. Use visualisation — plot the chosen vs. optimal decisions for small random instances. Look for patterns of local optimum traps.
Symptom · 04
After a data change (e.g., new coin denominations), greedy starts failing.
→
Fix
Re-validate the greedy choice property with the new data. Test with canonical coin check algorithm — if the coin system is not canonical, greedy fails.
★ Greedy Algorithm Debugging Cheat SheetQuick commands and checks to verify greedy correctness in production
Interval scheduling: too few activities selected−
Immediate action
Check sort order — did you sort by end time or start time?
Fragile — tiny change in problem constraints can break greedy
Robust — handles a wide range of constraints
Example Problems
Interval scheduling, Huffman, Dijkstra
Knapsack, Edit Distance, LCS
Production Risk
High if proof skipped
Low if subproblem space manageable
Key takeaways
1
Greedy makes the locally optimal choice at each step
no backtracking.
2
Greedy is correct when the problem has the greedy choice property
local optimal = global optimal.
3
Always prove correctness before trusting a greedy solution
or test with edge cases.
4
Interval scheduling (sort by end time) and Dijkstra are classic correct greedy algorithms.
5
Coin change with arbitrary denominations requires dynamic programming, not greedy.
Common mistakes to avoid
5 patterns
×
Using earliest start time for interval scheduling
Symptom
The algorithm selects fewer activities than optimal, often leaving large gaps of unused time between long early activities.
Fix
Sort by finish time instead. Verify with a counterexample like [(1,10), (2,3), (4,5)] — earliest start picks only first, earliest finish picks last two.
×
Assuming greedy works for coin change with arbitrary denominations
Symptom
Greedy returns more coins than necessary for certain amounts. E.g., with coins [1,3,4], amount 6 gives 3 coins (4+1+1) vs optimal 2 (3+3).
Fix
Implement a canonical coin system check. If not canonical, use dynamic programming. Test with all amounts up to a max value.
×
Forgetting to prove greedy choice property before deployment
Symptom
Silent suboptimal results in production that are hard to detect because the algorithm 'works' but not optimally. Gradual degradation of KPIs.
Fix
Integrate a counterexample generator into CI. For any greedy implementation, run a brute-force verifier for small N (≤10) on random inputs and assert equality.
×
Using greedy for weighted interval scheduling
Symptom
Greedy picks intervals with earliest finish time regardless of weight, missing high-value but slightly longer intervals. Profit is suboptimal.
Fix
Use dynamic programming for weighted interval scheduling. The greedy choice property does not hold when intervals have weights.
×
Applying greedy to the 0/1 Knapsack problem (non-fractional)
Symptom
Greedy by value/weight ratio fails because taking a fraction of an item is not allowed. Greedy may choose a high-density item that precludes two lower-density items that together have more total value.
Fix
Use DP for 0/1 Knapsack. Greedy only works for the Fractional Knapsack problem (items can be split).
INTERVIEW PREP · PRACTICE MODE
Interview Questions on This Topic
Q01SENIOR
What is the 'Greedy Choice Property' and how does it differ from 'Optima...
Q02SENIOR
Explain how the 'Jump Game' (LeetCode 55) can be solved using a greedy a...
Q03SENIOR
Why does the greedy approach for the 0/1 Knapsack problem fail while it ...
Q04SENIOR
Prove why sorting by 'Earliest Start Time' is a sub-optimal greedy strat...
Q05SENIOR
In Huffman Coding, what is the greedy choice being made at each step of ...
Q01 of 05SENIOR
What is the 'Greedy Choice Property' and how does it differ from 'Optimal Substructure'?
ANSWER
The greedy choice property means that a globally optimal solution can be arrived at by making a locally optimal (greedy) choice. Optimal substructure means that an optimal solution to the problem contains optimal solutions to its subproblems. Both are required for a greedy algorithm to be correct. Optimal substructure alone is not enough — many DP problems have it but greedy fails.
Q02 of 05SENIOR
Explain how the 'Jump Game' (LeetCode 55) can be solved using a greedy approach. What is the time complexity?
ANSWER
Jump Game asks: can you reach the last index given an array where each element is the maximum jump length from that position? The greedy approach maintains a variable maxReach — the furthest index reachable so far. Iterate through the array, updating maxReach as max(maxReach, i+nums[i]). If at any point i > maxReach, return false. After loop, return true. Time complexity O(N), space O(1). The greedy choice is to extend the reachable frontier as far as possible at each step.
Q03 of 05SENIOR
Why does the greedy approach for the 0/1 Knapsack problem fail while it works for the Fractional Knapsack?
ANSWER
Fractional Knapsack allows taking pieces of items, so taking the item with the best value/weight ratio at each step yields optimal total value. In 0/1 Knapsack, you must take the whole item or none. Greedy can pick a high-density item that blocks two lower-density items that together have higher total value. The problem lacks the greedy choice property because you cannot take a fraction of the blocker item.
Q04 of 05SENIOR
Prove why sorting by 'Earliest Start Time' is a sub-optimal greedy strategy for the Interval Scheduling problem.
ANSWER
Consider intervals: (1,10), (2,3), (4,5). Earliest start time picks (1,10) — only 1 activity. Optimal is (2,3) and (4,5) — 2 activities. The earliest start time picks a long interval that blocks many short ones. Earliest finish time avoids this because it leaves maximum residual time for other intervals. Formal proof: exchange argument shows that an optimal solution can be transformed to include the earliest-finishing interval.
Q05 of 05SENIOR
In Huffman Coding, what is the greedy choice being made at each step of the tree construction?
ANSWER
The greedy choice is to merge the two characters (or subtrees) with the smallest frequencies. This ensures that characters with low frequency get deeper (longer) codes, while high-frequency characters get shorter codes, minimising the total weighted path length (sum of frequency × depth). The optimal merge pattern guarantees optimality via an exchange argument.
01
What is the 'Greedy Choice Property' and how does it differ from 'Optimal Substructure'?
SENIOR
02
Explain how the 'Jump Game' (LeetCode 55) can be solved using a greedy approach. What is the time complexity?
SENIOR
03
Why does the greedy approach for the 0/1 Knapsack problem fail while it works for the Fractional Knapsack?
SENIOR
04
Prove why sorting by 'Earliest Start Time' is a sub-optimal greedy strategy for the Interval Scheduling problem.
SENIOR
05
In Huffman Coding, what is the greedy choice being made at each step of the tree construction?
SENIOR
FAQ · 6 QUESTIONS
Frequently Asked Questions
01
How do you prove a greedy algorithm is correct?
There are two standard proof methods. First is the 'Exchange Argument': you assume there is an optimal solution different from yours and prove that you can swap its elements with your greedy choices without making it worse. Second is 'Greedy Stays Ahead': you prove that at every step k, the greedy solution has progressed further toward the goal than any other possible algorithm.
Was this helpful?
02
What is the difference between greedy and dynamic programming?
The core difference is 'commitment.' A Greedy algorithm commits to a choice and never looks back. Dynamic Programming (DP) is more cautious; it calculates the results of all possible choices, stores them (memoization), and then picks the best path. Greedy is faster (O(N log N) or O(N)), while DP is more robust but more expensive (O(N²) or O(N × W)).
Was this helpful?
03
When should I definitely NOT use a Greedy algorithm?
If your current choice significantly limits or dictates your future options in a way that might be suboptimal, Greedy is likely to fail. A classic example is the Fractional Knapsack vs. 0/1 Knapsack. Greedy works for Fractional (you can take bits of items), but fails for 0/1 because taking a heavy, semi-valuable item might prevent you from taking two light, very valuable items later.
Was this helpful?
04
Is Dijkstra's algorithm greedy?
Yes. Dijkstra is a greedy algorithm because at each step it picks the 'closest' unvisited vertex to the source, trusting that this local shortest path will contribute to the global shortest path. This is why it fails with negative edge weights—the greedy property is broken.
Was this helpful?
05
What is a counterexample to greedy coin change with denominations 1, 3, 4?
Amount = 6. Greedy picks 4, then 1, then 1 (3 coins). Optimal is 3 and 3 (2 coins). This happens because greedy commits to the largest coin, missing the better 3+3 combination.
Was this helpful?
06
Can greedy algorithms be used in machine learning?
Yes, in algorithms like decision tree splitting (ID3 uses greedy entropy reduction), feature selection (forward selection is greedy), and reinforcement learning (ε-greedy policy). But they are often suboptimal; more sophisticated methods (beam search, Bayesian optimisation) are used when optimality matters.