Articles

Greedy Algorithms vs Dynamic Programming

Greedy algorithms commit to the locally best choice at each step; dynamic programming weighs every subproblem. When each one actually works.

The Lycoris Team The Lycoris Team · · 5 min read
A card catalog used for looking up records

A greedy algorithm builds a solution by making the locally best choice at each step and never looking back. Dynamic programming builds a solution by systematically considering every relevant subproblem and combining their optimal answers. Both are strategies for optimization problems, and both can produce genuinely optimal results — the difference is that greedy only works when a problem has a specific structural guarantee, and dynamic programming works more broadly but costs more to run.

How a greedy algorithm thinks

At each decision point, a greedy algorithm picks whatever option looks best right now, commits to it permanently, and moves to the next decision without reconsidering earlier choices. It never backtracks and never explores alternatives once a choice is made.

Making change with the fewest coins is the classic illustration. Given denominations of 25, 10, 5, and 1 cent, a greedy approach for 41 cents picks the largest coin that fits at each step: one 25, one 10, one 5, one 1 — four coins, which happens to be optimal for this denomination set. The greedy choice works here because U.S. coin denominations have a property that makes the locally best pick always compatible with a globally optimal solution.

Where greedy quietly breaks

Change that “just works” for U.S. coins can fail entirely for other denomination sets. Given coins of 1, 3, and 4, greedily making 6 cents picks a 4, then two 1s — three coins — when two 3s would only need two coins. Greedy commits to the 4 immediately because it’s the largest denomination that fits, without any way to reconsider once a better combination becomes visible in hindsight. This is the general risk with greedy algorithms: a locally optimal choice can foreclose a better global solution, and there’s no way to detect that without checking against other approaches or proving the greedy property holds for the specific problem.

Greedy algorithms are only guaranteed correct when a problem exhibits the greedy-choice property (a locally optimal choice is always part of some globally optimal solution) and optimal substructure (an optimal solution to the problem contains optimal solutions to its subproblems). Proving these hold is the actual work in justifying a greedy approach — without that proof, a greedy algorithm is just a fast heuristic that might happen to be wrong.

How dynamic programming thinks

Dynamic programming, covered in more depth in dynamic programming, explained, doesn’t commit to a single choice per step. Instead, it solves every relevant subproblem once, stores each result, and builds the final answer by combining those stored subproblem solutions — which lets it correctly handle cases where the locally attractive choice isn’t actually part of the best overall solution.

Applied to the 1-3-4 coin problem, a DP approach computes the minimum coins needed for every amount from 0 up to the target, using previously computed smaller amounts to build each larger one:

minCoins[0] = 0
minCoins[amount] = 1 + min(minCoins[amount - c] for each coin c <= amount)

For 6 cents, this correctly finds minCoins[3] + minCoins[3] = 1 + 1 = 2 coins, because it actually compares every combination rather than committing to the largest denomination first. The cost of that correctness is that DP has to compute and store an answer for every subproblem, not just walk straight to the end.

Side by side

GreedyDynamic programming
StrategyLocally best choice, no backtrackingSolve and cache every relevant subproblem
Correctness guaranteeOnly when the greedy-choice property provably holdsWhenever optimal substructure holds
Time complexityUsually faster — often a single passUsually slower — proportional to subproblem count
Space usageMinimal — no need to store subproblem resultsRequires memoization or a table of subproblem results
Failure modeCan silently produce a wrong answerCorrect but more expensive to compute
Classic examplesMinimum spanning tree, Huffman coding, interval schedulingFibonacci with memoization, edit distance, knapsack

When to reach for which

Use greedy when you can prove — or already know from established results — that the greedy-choice property holds for the specific problem: constructing a minimum spanning tree, building a Huffman coding tree, or scheduling non-overlapping intervals to maximize count are classic cases where greedy is both correct and considerably faster than a full DP formulation would be.

Use dynamic programming when subproblems overlap and the locally attractive choice isn’t reliably part of the global optimum — the 1-3-4 coin problem, the 0/1 knapsack problem, edit distance between two strings, or any optimization problem where an early “obviously good” choice can be shown to sometimes produce a worse final answer. If you’re unsure whether greedy applies, it usually doesn’t; the safer default is DP, with a greedy approach only substituted in once its correctness is actually established for that problem.

Complexity is the real trade-off

Reaching for DP by default isn’t free. A greedy algorithm often runs in time proportional to the number of decisions — frequently linear or log-linear, depending on how choices are ranked, as covered in Big O notation, explained. Dynamic programming’s cost scales with the number of distinct subproblems, which can be quadratic or worse depending on the problem’s structure. That gap is exactly why greedy is worth using whenever it’s provably correct — the speed difference compounds at scale, in the same way that quicksort vs mergesort or BFS vs DFS trade-offs come down to matching an algorithm’s guarantees to what a specific problem actually needs.

The takeaway

Greedy algorithms are fast because they never look back, but that speed only produces a correct answer when the problem has a provable greedy-choice property — otherwise an early “obviously good” pick can lock out the actual optimal solution. Dynamic programming is slower but correct more broadly, because it considers every relevant subproblem rather than committing early. When in doubt about whether a problem’s greedy choice is safe, default to dynamic programming, and only switch to greedy once its correctness for that specific problem is established.

The Lycoris Team The Lycoris Team · · 4 min read

The KMP Algorithm: Fast String Matching Explained

The Knuth-Morris-Pratt algorithm finds a pattern inside a text in linear time by never re-examining characters it has already matched.

#Computer Science #Algorithms #Data Structures
The Lycoris Team The Lycoris Team · · 5 min read

What Is a Ring Buffer?

A ring buffer is a fixed-size array that wraps its read and write pointers around, giving O(1) enqueue and dequeue without ever resizing.

#Computer Science #Algorithms #Data Structures
The Lycoris Team The Lycoris Team · · 5 min read

Fenwick Trees (Binary Indexed Trees), Explained

A Fenwick tree, or binary indexed tree, answers prefix-sum queries and point updates in O(log n) with far less memory than a segment tree.

#Computer Science #Algorithms #Data Structures