Why Greedy Works and When It Fails in DSA Typescript - Performance Analysis
We want to understand how fast greedy algorithms run and why their speed depends on the problem.
When does greedy give a quick solution, and when does it take longer or fail?
Analyze the time complexity of this simple greedy approach to the coin change problem.
function greedyCoinChange(coins: number[], amount: number): number {
let count = 0;
for (let i = coins.length - 1; i >= 0; i--) {
while (amount >= coins[i]) {
amount -= coins[i];
count++;
}
}
return count;
}
This code tries to use the largest coin first repeatedly until the amount is covered or no coins left.
Look at the loops and repeated steps:
- Primary operation: The inner while loop subtracting coins from amount.
- How many times: Up to amount divided by the smallest coin value.
As the amount grows, the number of subtractions grows roughly proportional to amount.
| Input Size (amount) | Approx. Operations |
|---|---|
| 10 | About 10 subtractions |
| 100 | About 100 subtractions |
| 1000 | About 1000 subtractions |
Pattern observation: The work grows roughly linearly with the amount size.
Time Complexity: O(n + m)
This means the time grows with the number of coin types (n) plus the amount (m), because the outer loop is O(n) and total inner iterations are O(m).
[X] Wrong: "Greedy always finds the best answer quickly for any problem."
[OK] Correct: Greedy works fast but can fail to find the best answer if the problem needs looking ahead or combining choices differently.
Understanding when greedy works helps you pick the right tool and explain your choices clearly in interviews.
"What if the coin denominations were not sorted or included tricky values? How would that affect the time complexity and correctness?"
Practice
Solution
Step 1: Understand greedy choice property
A greedy algorithm picks the best local option at each step without reconsidering previous choices.Step 2: Connect local choices to global optimum
For some problems, these local best choices lead to the overall best solution, which is why greedy works.Final Answer:
Because it makes the best choice at each step hoping for a global optimum -> Option CQuick Check:
Greedy = best local choice each step [OK]
- Thinking greedy tries all solutions
- Confusing greedy with recursion
- Assuming sorting always means greedy
arr?Solution
Step 1: Identify standard for loop syntax
The standard way to iterate over an array with index in TypeScript is usingfor (let i = 0; i < arr.length; i++).Step 2: Check other options for correctness
while (arr.length) { /* greedy step */ } and D use while/do-while but lack index control; for (const i in arr) { /* greedy step */ } usesfor...inwhich iterates keys but is less common and can cause issues.Final Answer:
for (let i = 0; i < arr.length; i++) { /* greedy step */ } -> Option AQuick Check:
Standard for loop = for (let i = 0; i < arr.length; i++) { /* greedy step */ } [OK]
- Using for...in for arrays causing unexpected keys
- Missing loop condition causing infinite loops
- Using while without index control
const intervals = [[1,4],[3,5],[0,6],[5,7],[8,9],[5,9]];
intervals.sort((a,b) => a[1] - b[1]);
let count = 0;
let end = -Infinity;
for (const [start, finish] of intervals) {
if (start >= end) {
count++;
end = finish;
}
}
console.log(count);What is the output?
Solution
Step 1: Sort intervals by finish time
Sorted intervals: [1,4], [3,5], [0,6], [5,7], [8,9], [5,9]Step 2: Select intervals greedily
Start with end = -Infinity.
Pick [1,4] (start 1 >= -Infinity), count=1, end=4.
Next [3,5] start 3 < 4 skip.
Next [0,6] start 0 < 4 skip.
Next [5,7] start 5 >= 4 pick, count=2, end=7.
Next [8,9] start 8 >= 7 pick, count=3, end=9.
Next [5,9] start 5 < 9 skip.Step 3: Count total selected intervals
Total count = 3Final Answer:
3 -> Option BQuick Check:
Greedy interval count = 3 [OK]
- Not sorting intervals by finish time
- Counting overlapping intervals
- Updating end incorrectly
const coins = [25, 10, 5, 1];
function greedyChange(amount: number): number {
let count = 0;
for (const coin of coins) {
count += Math.floor(amount / coin);
amount %= coin;
}
return count;
}
console.log(greedyChange(30));What is the output and what is the bug if any?
Solution
Step 1: Trace greedyChange(30)
Coins: 25,10,5,1
25 fits once (30/25=1), count=1, amount=30%25=5
10 fits zero times (5/10=0), count=1, amount=5
5 fits once (5/5=1), count=2, amount=0
1 fits zero times, count=2Step 2: Check if greedy works here
For US coins, greedy always gives minimum coins. So output is 2 and no bug here.Final Answer:
2; No bug, greedy works for US coins -> Option DQuick Check:
Greedy coin change = 2 [OK]
- Assuming greedy always fails coin change
- Miscomputing amount after division
- Confusing output count with amount
Solution
Step 1: Understand problem constraints
Activities have penalties if chosen consecutively, so simple greedy ignoring penalties may fail.Step 2: Choose algorithm considering penalties
Dynamic programming can track choices and penalties to find maximum benefit, unlike greedy which only looks locally.Final Answer:
Use dynamic programming to consider penalties and maximize total benefit -> Option AQuick Check:
Penalties require DP, not greedy [OK]
- Ignoring penalties and using greedy
- Assuming sorting by start time is enough
- Trying brute force without pruning
Ready to prove it?
Pass the test and this pattern moves unknown
