π° Greedy Algorithm β Comprehensive Study Guide
A **Greedy Algorithm** is an approach used to solve **optimization problems** by making the **locally optimal choice at each step** with the hope of reaching the **globally optimal solution**.
β‘ Why Use Greedy Algorithms?
- β **Faster than Dynamic Programming (O(n log n) or O(n)).**
- π **Used in scheduling, graph problems, and resource allocation.**
- π‘ **Provides approximate solutions for NP-Hard problems.**
- π **Simple and easy to implement.**
π How Does the Greedy Approach Work?
The greedy approach **works step by step**, making the best immediate choice. However, it **does not always guarantee an optimal solution** for all problems.
β **Conditions for a Problem to be Solved Using Greedy**
- π **Greedy Choice Property** β An optimal solution can be reached by making local choices.
- π **Optimal Substructure** β The optimal solution to a problem contains optimal solutions to its subproblems.
π₯ Common Greedy Algorithm Problems & Their Solutions
π **1. Activity Selection Problem (O(n log n))**
Find the maximum number of activities that can be done by one person.
struct Activity {
int start, end;
};
bool cmp(Activity a, Activity b) {
return a.end < b.end; // Sort by finish time
}
int activitySelection(vector& activities) {
sort(activities.begin(), activities.end(), cmp);
int count = 1, lastEnd = activities[0].end;
for (int i = 1; i < activities.size(); i++) {
if (activities[i].start >= lastEnd) {
count++;
lastEnd = activities[i].end;
}
}
return count;
}
π **2. Fractional Knapsack Problem (O(n log n))**
Choose items with maximum value-to-weight ratio.
struct Item {
int weight, value;
};
bool cmp(Item a, Item b) {
return (double)a.value / a.weight > (double)b.value / b.weight;
}
double fractionalKnapsack(vector- & items, int W) {
sort(items.begin(), items.end(), cmp);
double maxValue = 0.0;
for (auto item : items) {
if (W >= item.weight) {
maxValue += item.value;
W -= item.weight;
} else {
maxValue += item.value * ((double)W / item.weight);
break;
}
}
return maxValue;
}
π **3. Huffman Coding (O(n log n))**
Build an optimal prefix code for text compression.
struct Node {
char ch;
int freq;
Node *left, *right;
};
struct Compare {
bool operator()(Node* a, Node* b) {
return a->freq > b->freq; // Min-heap based on frequency
}
};
void huffmanCoding(vector>& data) {
priority_queue, Compare> pq;
for (auto item : data) pq.push(new Node{item.first, item.second, NULL, NULL});
while (pq.size() > 1) {
Node *left = pq.top(); pq.pop();
Node *right = pq.top(); pq.pop();
pq.push(new Node{'\0', left->freq + right->freq, left, right});
}
// The remaining node is the root of Huffman Tree
}
βοΈ Greedy vs Dynamic Programming
Feature | Greedy Algorithm | Dynamic Programming |
---|---|---|
Approach | Local optimal choice at each step | Solves subproblems & stores results |
Time Complexity | O(n log n) or O(n) | O(nΒ²) or O(nΒ³) |
Space Complexity | O(1) (In-place) | O(n) (Memoization/Tabulation) |
Use Cases | Graph algorithms, scheduling, compression | Longest paths, knapsack, text analysis |
β³ Time Complexity Analysis
- β **Best Case:** O(n log n) (Sorting-based Greedy problems).
- β‘ **Worst Case:** O(nΒ²) (In some graph-based problems).
π Real-World Applications of Greedy Algorithm
- π‘ **Network Routing (Primβs MST Algorithm).**
- π₯ **Video Compression (Huffman Coding).**
- π **Warehouse Optimization (Fractional Knapsack).**
- π **Task Scheduling (Activity Selection).**