Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
16 views13 pages

Greedy Algorithm Cheatsheet

cs

Uploaded by

youssefh.gamedev
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
16 views13 pages

Greedy Algorithm Cheatsheet

cs

Uploaded by

youssefh.gamedev
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 13

Greedy Algorithms Cheatsheet

1. Introduction

Greedy algorithms are a fundamental concept in computer science and algorithm


design. They are often used to solve optimization problems, where the goal is to find
the best possible solution among a set of feasible solutions. The core idea behind a
greedy algorithm is to make the locally optimal choice at each step with the hope that
this choice will lead to a globally optimal solution.

2. What is a Greedy Algorithm?

A greedy algorithm is an algorithmic paradigm that follows the problem-solving


heuristic of making the locally optimal choice at each stage with the hope of finding a
global optimum. In many problems, a greedy strategy does not produce an optimal
solution, but in some cases, it does. The main characteristic of a greedy algorithm is
that it makes decisions that are

"greedy" or "short-sighted" at each step, meaning it selects the best immediate option
without considering future consequences. This approach is often simpler and faster
than other optimization techniques, but it doesn't always guarantee the best overall
solution.

Characteristics of Greedy Algorithms:

Greedy Choice Property: A global optimum can be reached by making a locally


optimal (greedy) choice. This means that a locally optimal choice made at any
step will lead to a globally optimal solution. The choice made may depend on
previous choices but not on future choices or all the solutions to subproblems.

Optimal Substructure: An optimal solution to the problem contains optimal


solutions to subproblems. This property is also exploited by dynamic
programming, but greedy algorithms differ in that the choices are made in a
"greedy" fashion, and typically only one subproblem remains to be solved.
Components of a Greedy Algorithm:

1. Candidate Set: A set of items from which to choose a solution.

2. Selection Function: A function that chooses the best candidate to be added to


the solution set.

3. Feasibility Function: A function that determines if a candidate can be used to


contribute to a solution.

4. Objective Function: A function that assigns a value to a solution or a partial


solution.

5. Solution Function: A function that indicates whether a complete solution has


been reached.

3. How Greedy Algorithms Work

Greedy algorithms work by building a solution step-by-step, always choosing the


option that seems best at the current moment. The process typically involves:

1. Initialization: Start with an empty solution set.

2. Iteration: While the solution is not complete: a. Select the best candidate from
the available options using the selection function. b. Check if the selected
candidate is feasible using the feasibility function. c. If feasible, add the
candidate to the solution set.

3. Termination: Once a complete solution is formed (as determined by the solution


function), the algorithm terminates. The objective function is then used to
evaluate the quality of the solution.

It's crucial to understand that the "best" choice at each step is defined by the specific
problem and the objective function. The success of a greedy algorithm hinges on
whether these local optimal choices indeed lead to a global optimum. This is not
always the case, and proving the correctness of a greedy algorithm often involves
demonstrating the greedy choice property and optimal substructure.
4. Common Applications and Examples

Greedy algorithms are widely used in various domains due to their simplicity and
efficiency. Here are some classic examples:

4.1. Activity Selection Problem

Problem: Given a set of activities, each with a start and finish time, select the
maximum number of non-overlapping activities.

Greedy Approach: Sort activities by their finish times. Select the first activity, then
iteratively select the next activity that starts after the previously selected activity
finishes.

Why it works: Choosing the activity that finishes earliest leaves the maximum time
available for subsequent activities, thus maximizing the total number of activities.

4.2. Fractional Knapsack Problem

Problem: Given a set of items, each with a weight and a value, and a knapsack with a
maximum capacity, determine the items to include in the knapsack so that the total
value is maximized. Items can be broken into fractions.

Greedy Approach: Calculate the value-to-weight ratio for each item. Sort items in
descending order of this ratio. Iteratively add items (or fractions of items) with the
highest ratio until the knapsack capacity is reached.

Why it works: Prioritizing items with higher value density ensures that the knapsack is
filled with the most valuable items per unit of weight.

4.3. Dijkstra's Algorithm

Problem: Find the shortest path from a single source vertex to all other vertices in a
graph with non-negative edge weights.

Greedy Approach: At each step, Dijkstra's algorithm selects the unvisited vertex with
the smallest known distance from the source and adds it to the set of visited vertices. It
then updates the distances of its neighbors.
Why it works: By always choosing the closest unvisited vertex, the algorithm ensures
that the shortest path to that vertex has been found, and this choice does not prevent
finding shorter paths to other vertices.

4.4. Prim's Algorithm

Problem: Find a minimum spanning tree (MST) for a weighted undirected graph.

Greedy Approach: Start with an arbitrary vertex. At each step, add the cheapest edge
that connects a vertex in the MST to a vertex outside the MST.

Why it works: By always adding the cheapest available edge that expands the MST
without forming a cycle, the algorithm guarantees a minimum total weight for the
spanning tree.

4.5. Kruskal's Algorithm

Problem: Find a minimum spanning tree (MST) for a weighted undirected graph.

Greedy Approach: Sort all edges in non-decreasing order of their weights. Iteratively
add edges to the MST if they do not form a cycle with already added edges, until V-1
edges are added (where V is the number of vertices).

Why it works: Similar to Prim's, by always considering the cheapest available edge
and ensuring no cycles are formed, Kruskal's algorithm constructs an MST.

4.6. Huffman Coding

Problem: Construct a prefix code (binary code where no code is a prefix of another) for
a set of characters with given frequencies, such that the average code length is
minimized.

Greedy Approach: Build the Huffman tree from the bottom up. Repeatedly combine
the two nodes with the smallest frequencies until only one node (the root) remains.

Why it works: Combining the least frequent characters first ensures that they have
longer code lengths, while more frequent characters have shorter code lengths,
leading to an optimal average code length.
5. Advantages and Disadvantages of Greedy
Algorithms

5.1. Advantages

Simplicity: Greedy algorithms are often much simpler to design and implement
compared to other algorithmic paradigms like dynamic programming or
backtracking.

Efficiency: In many cases, greedy algorithms are more efficient in terms of time
complexity, as they make decisions quickly without re-evaluating past choices.

Intuitive: The logic behind greedy choices can be very intuitive and easy to
understand for certain problems.

5.2. Disadvantages

Not Always Optimal: The biggest drawback is that a greedy approach does not
always guarantee a globally optimal solution. A locally optimal choice might lead
to a suboptimal or even incorrect overall solution.

Difficulty in Proving Correctness: Proving that a greedy algorithm yields an


optimal solution can be challenging and often requires demonstrating the greedy
choice property and optimal substructure.

Limited Applicability: Greedy algorithms are only suitable for problems that
exhibit the greedy choice property and optimal substructure. Many problems do
not satisfy these conditions.

6. When to Use Greedy Algorithms

Greedy algorithms are best suited for problems where:

Greedy Choice Property Holds: A locally optimal choice at each step leads to a
globally optimal solution.

Optimal Substructure Exists: An optimal solution to the problem contains


optimal solutions to subproblems.
Simplicity and Speed are Prioritized: When a quick and reasonably good
solution is acceptable, even if it's not strictly optimal.

Problem Structure Allows: Problems like activity selection, fractional knapsack,


and minimum spanning tree problems are classic examples where greedy works.

7. When Not to Use Greedy Algorithms

Avoid using greedy algorithms when:

Greedy Choice Does Not Guarantee Optimality: If making a locally optimal


choice can lead to a suboptimal global solution (e.g., 0/1 Knapsack Problem).

Future Choices Depend on Past Decisions in Complex Ways: When decisions


made at one step significantly impact the feasibility or optimality of future
choices in a non-trivial manner.

All Possible Solutions Need to be Explored: For problems requiring exploration


of all possible solutions to guarantee optimality (e.g., Traveling Salesperson
Problem, which often requires dynamic programming or backtracking).

8. Conclusion

Greedy algorithms offer an elegant and efficient approach to solving a subset of


optimization problems. While their simplicity and speed are attractive, it is crucial to
understand their limitations and the specific properties a problem must possess for a
greedy strategy to yield an optimal solution. When applicable, greedy algorithms
provide powerful and practical solutions, making them an essential tool in any
computer scientist's arsenal.
Activity Selection Problem Diagram

Fractional Knapsack Problem Diagram


Dijkstra's Algorithm Diagram

Prim's Algorithm Diagram


Kruskal's Algorithm Diagram

Huffman Coding Diagram

9. More Examples with C++ Code

9.1. Coin Change Problem (Greedy Approach)

Problem: Given a set of coin denominations and an amount, find the minimum
number of coins required to make up that amount. Assume an infinite supply of each
coin denomination. This greedy approach works for certain coin systems (e.g., US
currency) but not all.
Greedy Approach: Sort the coin denominations in descending order. Iterate through
the denominations, and for each denomination, take as many coins as possible
without exceeding the remaining amount. Subtract the value of the taken coins from
the amount and continue with the next denomination.

Why it works (for canonical coin systems): For canonical coin systems (like the US
dollar system), taking the largest possible coin at each step leads to the optimal
solution. However, for non-canonical systems, this greedy approach might not yield
the optimal solution.

C++ Implementation:

#include <iostream>
#include <vector>
#include <algorithm>

int findMinCoins(std::vector<int>& coins, int amount) {


std::sort(coins.rbegin(), coins.rend()); // Sort coins in descending order

int numCoins = 0;
std::cout << "Coins used: ";
for (int i = 0; i < coins.size(); ++i) {
while (amount >= coins[i]) {
amount -= coins[i];
numCoins++;
std::cout << coins[i] << " ";
}
}
std::cout << std::endl;
return numCoins;
}

int main() {
std::vector<int> coins = {1, 2, 5, 10, 20, 50, 100, 200, 500, 2000}; //
Indian currency denominations
int amount = 257;

std::cout << "Amount: " << amount << std::endl;


int minCoins = findMinCoins(coins, amount);
std::cout << "Minimum coins required: " << minCoins << std::endl;

std::vector<int> us_coins = {1, 5, 10, 25}; // US currency denominations


int us_amount = 63;

std::cout << "\nAmount: " << us_amount << std::endl;


minCoins = findMinCoins(us_coins, us_amount);
std::cout << "Minimum coins required: " << minCoins << std::endl;

return 0;
}

Output for the above code:


Amount: 257
Coins used: 200 50 5 2
Minimum coins required: 4

Amount: 63
Coins used: 25 25 10 1 1 1
Minimum coins required: 6

9.2. Job Sequencing Problem with Deadlines

Problem: Given a set of jobs, where each job has a deadline and a profit, find a
sequence of jobs that can be performed within their deadlines to maximize the total
profit. Each job takes one unit of time.

Greedy Approach: Sort the jobs in descending order of their profits. Iterate through
the sorted jobs. For each job, try to schedule it in the latest possible time slot before its
deadline (and before any other already scheduled job). If a slot is available, schedule
the job; otherwise, skip it.

Why it works: By prioritizing jobs with higher profits and scheduling them as late as
possible, we leave earlier slots open for other potentially profitable jobs that might
have earlier deadlines. This greedy choice aims to maximize the total profit.

C++ Implementation:
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>

struct Job {
char id; // Job Id
int deadline; // Deadline of job
int profit; // Profit if job is completed by or before deadline
};

// Comparison function to sort jobs by profit in descending order


bool comparison(Job a, Job b) {
return (a.profit > b.profit);
}

void printJobScheduling(std::vector<Job>& arr) {


int n = arr.size();

// Sort all jobs according to decreasing order of profit


std::sort(arr.begin(), arr.end(), comparison);

int maxDeadline = 0;
for (int i = 0; i < n; i++) {
if (arr[i].deadline > maxDeadline) {
maxDeadline = arr[i].deadline;
}
}

std::vector<int> result(maxDeadline + 1, -1); // To store result (sequence


of jobs)
std::vector<bool> slot(maxDeadline + 1, false); // To keep track of free
time slots

// Iterate through all given jobs


for (int i = 0; i < n; i++) {
// Find a free slot for this job (starting from the last possible slot)
for (int j = arr[i].deadline; j > 0; j--) {
// Free slot found
if (slot[j] == false) {
result[j] = i; // Add this job to result
slot[j] = true; // Mark this slot filled
break;
}
}
}

// Print the result


std::cout << "Following is maximum profit sequence of jobs: ";
for (int i = 1; i <= maxDeadline; i++) {
if (slot[i]) {
std::cout << arr[result[i]].id << " ";
}
}
std::cout << std::endl;
}

int main() {
std::vector<Job> jobs = {{'a', 2, 100}, {'b', 1, 19}, {'c', 2, 27}, {'d',
1, 25}, {'e', 3, 15}};
printJobScheduling(jobs);
return 0;
}

Output for the above code:

Following is maximum profit sequence of jobs: c a e

10. Resources

Here are some of the resources used in compiling this cheatsheet:

Greedy Algorithm - Wikipedia: https://en.wikipedia.org/wiki/Greedy_algorithm

Greedy Algorithms - GeeksforGeeks:


https://www.geeksforgeeks.org/dsa/greedy-algorithms/

What is a Greedy Algorithm? Examples of Greedy Algorithms -


freeCodeCamp: https://www.freecodecamp.org/news/greedy-algorithms/

Greedy Algorithm - Programiz: https://www.programiz.com/dsa/greedy-


algorithm

Activity Selection Problem - CodeCrucks: https://codecrucks.com/activity-


selection-problem/

Fractional Knapsack Problem - Hello Algo: https://www.hello-


algo.com/chapter_greedy/fractional_knapsack_problem/

What is Dijkstra's Algorithm? - GeeksforGeeks:


https://www.geeksforgeeks.org/what-is-dijkstras-algorithm/

Prim's Algorithm for Minimum Spanning Tree (MST) - GeeksforGeeks:


https://www.geeksforgeeks.org/prims-algorithm-for-minimum-spanning-tree-
mst/

Kruskal's Minimum Spanning Tree (MST) Algorithm - GeeksforGeeks:


https://www.geeksforgeeks.org/kruskals-minimum-spanning-tree-mst-
algorithm/

Huffman Coding | Greedy Algo-3 - GeeksforGeeks:


https://www.geeksforgeeks.org/huffman-coding-greedy-algo-3/

You might also like