Greedy vs DP How to Know Which to Apply in DSA C++ - Complexity Comparison
We want to understand how to decide between greedy and dynamic programming (DP) approaches by looking at their time costs.
Which method runs faster and when does that matter?
Analyze the time complexity of these two approaches solving the same problem.
// Greedy approach example (simplified)
int greedySolution(std::vector<int> &arr) {
int result = 0;
for (int i = 0; i < arr.size(); i++) {
if (canTake(arr[i])) {
result += arr[i];
}
}
return result;
}
// DP approach example (simplified)
int dpSolution(std::vector<int> &arr) {
std::vector<int> dp(arr.size() + 1, 0);
dp[0] = 0;
dp[1] = arr[0];
for (int i = 2; i <= arr.size(); i++) {
dp[i] = std::max(dp[i-1], dp[i-2] + arr[i-1]);
}
return dp[arr.size()];
}
The greedy code picks items quickly by checking a simple rule once per item. The DP code builds a table considering past choices for each item.
Look at what repeats in each approach.
- Greedy primary operation: One loop over all items, checking a simple condition once per item.
- Greedy how many times: Exactly once per item, so n times.
- DP primary operation: One loop over all items, but each step uses previous results stored in dp array.
- DP how many times: Exactly once per item, also n times, but with extra work to compare and add.
Both approaches loop through the input list once, but DP does a bit more work per item.
| Input Size (n) | Greedy Approx. Operations | DP Approx. Operations |
|---|---|---|
| 10 | ~10 checks | ~10 comparisons and additions |
| 100 | ~100 checks | ~100 comparisons and additions |
| 1000 | ~1000 checks | ~1000 comparisons and additions |
Pattern observation: Both grow linearly with input size, but DP does slightly more work per step.
Time Complexity: O(n)
This means both methods take time proportional to the number of items, but DP has a bit more work inside the loop.
[X] Wrong: "Greedy is always faster and better than DP because it's simpler."
[OK] Correct: Greedy is faster only when its simple rule always leads to the best answer. If the problem needs to consider past choices, DP is needed despite extra work.
Knowing when to pick greedy or DP shows you understand problem patterns and efficiency, a key skill in coding challenges and real projects.
"What if the DP solution used recursion with memoization instead of a loop? How would the time complexity change?"
Practice
Solution
Step 1: Understand greedy algorithm conditions
Greedy algorithms work well when the problem has an optimal substructure and no overlapping subproblems, meaning making the best local choice leads to a global optimum.Step 2: Compare with dynamic programming conditions
Dynamic programming is used when there are overlapping subproblems and optimal substructure, requiring storing intermediate results to avoid recomputation.Final Answer:
When the problem has optimal substructure and no overlapping subproblems -> Option BQuick Check:
Greedy = optimal substructure + no overlap [OK]
- Confusing overlapping subproblems as a greedy condition
- Using DP when greedy suffices
- Ignoring optimal substructure requirement
Solution
Step 1: Check syntax for array initialization
int dp[5]; leaves elements uninitialized. int dp(5, 0); is invalid syntax for array initialization. int dp = new int[5]; uses incorrect syntax and memory management in C++.Step 2: Identify correct vector initialization
vector<int> dp(5, 0); correctly creates a vector of size 5 with all elements initialized to 0, which is the preferred way to initialize DP arrays in modern C++.Final Answer:
vector<int> dp(5, 0); -> Option CQuick Check:
Use vector<int> dp(size, 0) for DP arrays [OK]
- Using incorrect syntax for array initialization
- Not initializing all elements to zero
- Using raw pointers without delete
int main() {
int arr[] = {1, 2, 3, 4, 5};
int n = 5;
int sum = 0;
for (int i = 0; i < n; i++) {
if (arr[i] % 2 == 0) {
sum += arr[i];
}
}
std::cout << sum << std::endl;
return 0;
}Solution
Step 1: Identify even numbers in the array
The array is {1, 2, 3, 4, 5}. Even numbers are 2 and 4.Step 2: Calculate sum of even numbers
Sum = 2 + 4 = 6.Final Answer:
6 -> Option AQuick Check:
Sum of even numbers = 6 [OK]
- Summing all numbers instead of only even
- Off-by-one error in loop
- Misunderstanding modulo operator
int coinChange(int amount) {
int coins[] = {25, 10, 5, 1};
int count = 0;
for (int i = 0; i < 4; i++) {
while (amount >= coins[i]) {
amount -= coins[i];
count++;
}
}
return count;
}Solution
Step 1: Understand greedy approach for coin change
The greedy approach picks the largest coin repeatedly. It works only if coin denominations are canonical (like US coins).Step 2: Identify problem with greedy for arbitrary coins
For some coin sets, greedy does not give minimum coins. Dynamic programming is needed for guaranteed optimality.Final Answer:
Greedy approach fails for some coin sets, not always optimal -> Option AQuick Check:
Greedy coin change not always optimal [OK]
- Assuming greedy always gives minimum coins
- Ignoring coin set properties
- Confusing loop correctness with algorithm correctness
struct Task {
int deadline;
int profit;
};Solution
Step 1: Understand task scheduling problem
This is a classic greedy problem where tasks are sorted by profit descending, then scheduled in the latest free slot before deadline.Step 2: Compare approaches
Dynamic programming or brute force is too slow. Sorting by deadline only does not maximize profit. Greedy by profit with latest slot is efficient and optimal.Final Answer:
Use greedy algorithm by sorting tasks by profit and scheduling latest available slot -> Option DQuick Check:
Greedy by profit + latest slot = max profit [OK]
- Sorting only by deadline misses max profit
- Trying DP or brute force unnecessarily
- Scheduling tasks without checking deadlines
Ready to prove it?
Pass the test and this pattern moves unknown
