Greedy algorithms are a class of algorithms that make the locally optimal choice at each step with
the hope of finding a global optimum. Unlike dynamic programming, greedy algorithms do not revisit
or store the results of previous subproblems.
The greedy approach works when a problem exhibits the greedy-choice property (a globally optimal
solution can be arrived at by choosing the local optimum at each step) and optimal substructure (an
optimal solution can be constructed from optimal solutions of its subproblems).
Greedy algorithms are typically easier to implement and more efficient than dynamic programming,
but they do not guarantee the best solution for all problems. They are suitable for problems like
activity selection, Huffman encoding, minimum spanning tree (Kruskal's and Prim's algorithms), and
Dijkstra's shortest path algorithm.
When designing a greedy algorithm, it's important to:
1. Prove the problem has the greedy-choice property.
2. Justify that choosing local optima leads to the global optimum.
3. Ensure the algorithm covers all edge cases and constraints.
Despite their simplicity, greedy algorithms are powerful and effective when used in the right context.