Skip to main content
Greedy Pattern

What is Greedy?

Greedy algorithms make the locally optimal choice at each step, hoping to find a global optimum. They work when local choices lead to global solutions (greedy choice property + optimal substructure).
Quick Recognition: Optimization problems where taking the “best” option at each step leads to the global best. Look for keywords: “minimum number of”, “maximum profit”, “optimal selection”.

Pattern Recognition Checklist

Use Greedy When

  • Interval scheduling/merging
  • Minimum coins (certain denominations)
  • Jump game style problems
  • Huffman encoding
  • Activity selection
  • Fractional knapsack

Don't Use When

  • 0/1 Knapsack (need DP)
  • Coin change (arbitrary denominations)
  • Need to try all possibilities
  • Local optimal may miss global
  • Problem has overlapping subproblems

Greedy vs DP: How to Choose

CharacteristicGreedyDP
Makes irrevocable choicesYesNo, tries all
Optimal substructureRequiredRequired
Overlapping subproblemsNot handledHandled via caching
Time complexityUsually O(n log n)Usually O(n^2) or more
Proof requiredYes (exchange argument)No, exhaustive

Common Greedy Patterns

PatternGreedy ChoiceExample
Interval SchedulingSort by end timeNon-overlapping intervals
Interval PartitioningSort by start timeMeeting rooms
Fractional KnapsackBest value/weight ratioMaximize value
Job SchedulingSort by deadlineMinimize late jobs
Huffman CodingMerge least frequentOptimal prefix codes

When to Use

Scheduling

Task scheduling, meeting rooms, intervals

Selection

Choosing items to maximize/minimize value

Graph Problems

MST (Kruskal, Prim), shortest paths (Dijkstra)

String/Array

Partition labels, jump game

Pattern Variations

1. Interval Scheduling

def max_non_overlapping_intervals(intervals):
    """Maximum number of non-overlapping intervals"""
    if not intervals:
        return 0
    
    # Sort by end time (greedy choice: earliest ending first)
    intervals.sort(key=lambda x: x[1])
    
    count = 1
    end = intervals[0][1]
    
    for i in range(1, len(intervals)):
        if intervals[i][0] >= end:  # No overlap
            count += 1
            end = intervals[i][1]
    
    return count

2. Activity Selection (Meeting Rooms)

def min_meeting_rooms(intervals):
    """Minimum rooms needed for all meetings"""
    if not intervals:
        return 0
    
    # Track start and end times separately
    starts = sorted([i[0] for i in intervals])
    ends = sorted([i[1] for i in intervals])
    
    rooms = 0
    max_rooms = 0
    s, e = 0, 0
    
    while s < len(starts):
        if starts[s] < ends[e]:
            rooms += 1
            s += 1
        else:
            rooms -= 1
            e += 1
        max_rooms = max(max_rooms, rooms)
    
    return max_rooms

3. Jump Game

def can_jump(nums):
    """Can we reach the last index?"""
    max_reach = 0
    
    for i in range(len(nums)):
        if i > max_reach:
            return False
        max_reach = max(max_reach, i + nums[i])
    
    return True

def min_jumps(nums):
    """Minimum jumps to reach last index"""
    n = len(nums)
    if n <= 1:
        return 0
    
    jumps = 0
    current_end = 0
    farthest = 0
    
    for i in range(n - 1):
        farthest = max(farthest, i + nums[i])
        
        if i == current_end:
            jumps += 1
            current_end = farthest
    
    return jumps

4. Gas Station

def can_complete_circuit(gas, cost):
    """Find starting gas station to complete circuit"""
    total_tank = 0
    current_tank = 0
    start = 0
    
    for i in range(len(gas)):
        total_tank += gas[i] - cost[i]
        current_tank += gas[i] - cost[i]
        
        if current_tank < 0:
            start = i + 1
            current_tank = 0
    
    return start if total_tank >= 0 else -1

5. Partition Labels

def partition_labels(s):
    """Partition string so each letter appears in at most one part"""
    # Find last occurrence of each character
    last = {c: i for i, c in enumerate(s)}
    
    result = []
    start = 0
    end = 0
    
    for i, c in enumerate(s):
        end = max(end, last[c])
        
        if i == end:
            result.append(end - start + 1)
            start = i + 1
    
    return result

Classic Problems

Pattern: Sort by end time, greedily select non-overlappingKey: Earliest end time leaves most room for future
Pattern: Track maximum reachable indexKey: At each position, update farthest reach
Pattern: Process most frequent tasks firstKey: Idle time depends on most frequent task
Pattern: Two passes (left to right, right to left)Key: Satisfy constraints from both directions

Common Mistakes

Avoid These Pitfalls:
  1. Wrong greedy choice: Not all locally optimal leads to global optimal
  2. Missing proof: Greedy needs proof of correctness (exchange argument)
  3. Wrong sorting key: End time vs start time matters for intervals
  4. Edge cases: Empty input, single element, all same values

How to Prove Greedy Works

1

Greedy Stays Ahead

Show that after each step, greedy is at least as good as optimal
2

Exchange Argument

Show that swapping a choice in optimal with greedy’s choice doesn’t hurt
3

Structural Induction

Prove base case + if true for size k, true for size k+1

Interview Problems by Company

ProblemCompanyKey Concept
Assign CookiesAmazonSort both arrays
Lemonade ChangeGoogleTrack denominations
Best Time to Buy StockAll FAANGTrack minimum so far

Interview Tips

Script for interviews:
  1. “This looks like a greedy problem because local choices lead to global optimum.”
  2. “My greedy strategy is: [describe the choice]”
  3. “This works because [brief justification]”
  4. “I’ll sort by [criteria] first, then iterate greedily.”
  5. “Time is O(n log n) for sorting, O(n) for the greedy pass.”
Interviewer SaysYou Should Think
”Minimum operations”Greedy or DP
”Schedule tasks/meetings”Interval scheduling
”Maximum profit/value”Greedy or DP
”Prove it’s optimal”Exchange argument
”What if greedy fails?”Consider DP
def solve_greedy(items):
    # Step 1: Sort by greedy criteria
    items.sort(key=lambda x: x.end_time)  # or other criteria
    
    result = 0
    current_state = initial_state
    
    # Step 2: Make greedy choice at each step
    for item in items:
        if can_include(item, current_state):
            result += 1
            current_state = update(current_state, item)
    
    return result

Practice Problems

ProblemDifficultyLink
Jump GameMediumLeetCode 55
Gas StationMediumLeetCode 134
Partition LabelsMediumLeetCode 763
Task SchedulerMediumLeetCode 621
Non-overlapping IntervalsMediumLeetCode 435

Practice Roadmap

1

Day 1: Basic Greedy

  • Solve: Assign Cookies, Lemonade Change
  • Focus: Simple greedy choices
2

Day 2: Interval Problems

  • Solve: Non-overlapping Intervals, Meeting Rooms
  • Focus: Sort by end time pattern
3

Day 3: Array Greedy

  • Solve: Jump Game I and II, Gas Station
  • Focus: Tracking reachability
4

Day 4: Complex Greedy

  • Solve: Task Scheduler, Candy
  • Focus: Multi-pass greedy
Interview Tip: Before applying greedy, ask yourself: “Does taking the best choice now prevent a better overall solution?” If yes, consider DP instead.