Pattern

Greedy

Build the optimal solution one step at a time by always choosing the locally best option, without looking back.

Roadmap

  1. What Is This Pattern?
  2. When to Use It
  3. Variant 1 — Interval Scheduling / Activity Selection
  4. Variant 2 — Greedy with Sorting (Jump Game style)
  5. Template Code
  6. Visual Walkthrough
  7. Common Problems
  8. Interactive Demo
  9. Complexity Analysis
  10. Key Takeaways
Step 01

What Is This Pattern?

A greedy algorithm builds its solution piece by piece, always choosing the option that looks best right now. It never reconsiders a past choice. The magic: for the right class of problems, local optimality leads to global optimality.

The Core Idea

At each step, make the choice that maximizes (or minimizes) the immediate gain. Unlike dynamic programming, you do not explore every possible subproblem. You commit once and move on.

Greedy

Pick best local choice
O(n) or O(n log n)
No backtracking
One path through choices
A
|
B
|
C

Exhaustive (DP / Backtracking)

Explore all combinations
O(2^n) or O(n^2)
Tries every path
Exponential branches
?
/   \
A
B
/\    /\
...
...
...
...
💡

Why does this work? Greedy algorithms require the greedy choice property: there exists an optimal solution that includes the greedy (locally best) choice. They also require optimal substructure: after making the greedy choice, the remaining problem is a smaller instance of the same type. When both hold, greedily choosing at every step assembles the global optimum.

Step 02

When to Use It

Look for these recognition signals in the problem statement. If you spot one, a greedy approach is very likely the intended (or at least a valid) approach.

"Maximum number of events/activities"
Scheduling problems where you want to attend as many non-overlapping events as possible. Sort by end time.
"Minimum number of intervals to remove"
The flip side of activity selection: remove the fewest intervals so the rest don't overlap.
"Can you reach the end?" or "Minimum jumps"
Reachability problems where each position gives you a range. Track the farthest reachable index greedily.
"Partition into fewest groups" or "assign greedily"
Partitioning problems where sorting + a single scan yields optimal groupings (e.g., Partition Labels).

The key clue: The problem asks for an optimal value (max/min) and you suspect that always picking the locally best option will never box you into a worse global outcome. If a counterexample breaks the greedy, use DP instead. If no counterexample exists, greedy is the way.

Step 03

Variant 1 — Interval Scheduling / Activity Selection

Sort by End Time

Sort intervals by their end time. Greedily pick each interval whose start time is at or after the previous interval's end. By always picking the interval that finishes earliest, you leave the most room for future intervals.

Walkthrough: [[1,3], [2,4], [3,5], [0,6], [5,7], [8,9]]

Find the maximum number of non-overlapping intervals.

After sorting by end time
[1,3], [2,4], [3,5], [0,6], [5,7], [8,9]
Sorted. Start scanning from the first interval.
0
picked
[1, 3]
Pick. Start 1 ≥ prevEnd 0. Update prevEnd = 3. count = 1
1
picked
[2, 4]
Skip. Start 2 < prevEnd 3. Overlaps with [1,3].
1
picked
[3, 5]
Pick. Start 3 ≥ prevEnd 3. Update prevEnd = 5. count = 2
2
picked
[0, 6]
Skip. Start 0 < prevEnd 5. Overlaps.
2
picked
[5, 7]
Pick. Start 5 ≥ prevEnd 5. Update prevEnd = 7. count = 3
3
picked
[8, 9]
Pick. Start 8 ≥ prevEnd 7. Update prevEnd = 9. count = 4
Maximum non-overlapping intervals: 4
4
picked
🎯

Why sort by end time? The interval that ends earliest leaves the maximum remaining time for future intervals. If you sorted by start time or by length, you might pick an interval that blocks many shorter ones. Sorting by end time is provably optimal for this class of problems.

Step 04

Variant 2 — Greedy with Sorting (Jump Game style)

Track Farthest Reach

Scan the array left to right, maintaining the farthest index you can reach. At each position, update the farthest reach using max(farthest, i + nums[i]). If you ever find that i > farthest, you are stuck and cannot reach the end.

Example: [2, 3, 1, 1, 4]

Can you reach the last index? Track the farthest reachable position.

i=0: farthest = max(0, 0+2) = 2. Can reach index 2.
i=1: 1 ≤ 2, reachable. farthest = max(2, 1+3) = 4. Can reach index 4.
i=2: 2 ≤ 4, reachable. farthest = max(4, 2+1) = 4. No change.
i=3: 3 ≤ 4, reachable. farthest = max(4, 3+1) = 4. No change.
farthest (4) ≥ last index (4). Reachable!

Counterexample: [3, 2, 1, 0, 4]

Here the greedy approach correctly detects that you are stuck.

i=0: farthest = max(0, 0+3) = 3.
i=1: farthest = max(3, 1+2) = 3. No change.
i=2: farthest = max(3, 2+1) = 3. No change.
i=3: farthest = max(3, 3+0) = 3. nums[3]=0, stuck!
i=4: 4 > farthest (3). Cannot reach index 4. Unreachable
💡

Extending to "minimum jumps": For Jump Game II (#45), add a second variable currentEnd that tracks the boundary of the current jump. When i reaches currentEnd, you must make another jump (increment the count) and set currentEnd = farthest. Same greedy scan, one extra variable.

Step 05

Template Code

Here are annotated templates for the two major greedy variants. Both run in linear time after any initial sort.

Jump Game (Reachability)

// Can we reach the last index?
boolean canJump(int[] nums) {
    int farthest = 0;

    for (int i = 0; i < nums.length; i++) {
        if (i > farthest) return false;  // stuck, can't reach i
        farthest = Math.max(farthest, i + nums[i]);
    }
    return true;                      // farthest >= last index
}

Activity Selection (Interval Scheduling)

// Maximum non-overlapping intervals
int maxActivities(int[][] intervals) {
    Arrays.sort(intervals, (a, b) -> a[1] - b[1]); // sort by end

    int count = 0, prevEnd = Integer.MIN_VALUE;
    for (int[] iv : intervals) {
        if (iv[0] >= prevEnd) {           // no overlap
            count++;
            prevEnd = iv[1];              // commit to this interval
        }
    }
    return count;
}
📝

The sorting step is critical. Most greedy algorithms require a preprocessing sort to establish the order in which choices are evaluated. Without it, the locally best choice at each step might not lead to a globally optimal answer. The sort gives the greedy scan the structure it needs.

Step 06

Visual Walkthrough

Let us trace Jump Game II through a larger example step by step, showing the farthest reach and jump count at every stage.

Array: [2, 3, 1, 1, 4] — Minimum Jumps

Finding the minimum number of jumps to reach the last index.

STEP ACTION FARTHEST CUR END JUMPS
init Initialize variables 0 0 0
i=0 (2) farthest = max(0, 0+2) = 2. i == curEnd, jump! curEnd = 2 2 2 1
i=1 (3) farthest = max(2, 1+3) = 4. i < curEnd, no jump yet. 4 2 1
i=2 (1) farthest = max(4, 2+1) = 4. i == curEnd, jump! curEnd = 4 4 4 2
done curEnd (4) ≥ last index (4). Stop. 4 4 2
REACHABILITY MAP
2
3
1
1
4
Minimum jumps: 2 (index 0 → 1 → 4)
💡

Notice the pattern: We never actually "jump" in a specific direction. Instead we track the boundary of the current jump. When we reach the boundary, we must take another jump and extend the boundary to the farthest we discovered so far. This implicit BFS-by-level technique is what keeps it O(n).

Step 07

Common Problems

These are classic LeetCode problems that use the greedy pattern, listed in rough order of difficulty.

#55 Jump Game Medium
#45 Jump Game II Medium
#134 Gas Station Medium
#435 Non-overlapping Intervals Medium
#763 Partition Labels Medium
#1353 Maximum Number of Events That Can Be Attended Medium
💡

Practice tip: Start with #55 (direct application of the farthest-reach template). Then try #45 which adds the "minimum jumps" layer. Move to #435 for interval scheduling (the flip side of activity selection: minimize removals = maximize kept intervals). #763 Partition Labels is a beautiful greedy problem that combines last-occurrence tracking with interval merging.

Step 08

Interactive Demo

Enter an array of jump lengths and step through the greedy algorithm. Watch the farthest reachable position grow and see which indices are reachable.

⚙ Jump Game Visualizer


ARRAY
REACH BAR
0
index
Press "Step →" or "Run All" to begin.
Step 09

Complexity Analysis

Time
O(n)
Space
O(1)

Why O(n) Time and O(1) Space?

The Jump Game variants scan the array exactly once with a single for loop. At each index, we do O(1) work: one comparison and one max() call. Total: O(n).

We only track two or three integer variables (farthest, currentEnd, jumps). No auxiliary arrays or stacks. Total space: O(1).

For interval scheduling: The sort takes O(n log n) and the scan takes O(n), so the total is O(n log n). Space is O(1) beyond the input (or O(n) if the sort is not in-place).

Greedy vs DP trade-off: Many greedy problems can also be solved with DP, but the DP solution is typically O(n^2) time and O(n) space. The greedy approach achieves O(n) time and O(1) space because it collapses the entire DP table into one or two running variables. This is the reward for proving that the greedy choice property holds.

Step 10

Key Takeaways

01

Greedy = local optimality leads to global optimality. At each step, you make the best available choice without reconsidering past decisions. This only works when the problem has the greedy choice property and optimal substructure.

02

Sort first, then scan. Most greedy problems require sorting by a key metric (end time, deadline, ratio, etc.) before the greedy scan. The sort establishes the order that makes greedy choices safe.

03

Track one or two running variables. The farthest-reach pattern (Jump Game) collapses the entire state space into farthest and currentEnd. Activity selection just needs prevEnd and count. Greedy solutions are elegant because they are minimal.

04

If you cannot prove correctness, try a counterexample. The hardest part of greedy is knowing when it works. Try to construct a small input where the greedy choice leads to a worse outcome. If you can, use DP. If you cannot, the greedy is very likely correct.

05

Interval problems are the bread and butter of greedy. Activity selection, non-overlapping intervals, meeting rooms, and merge intervals all follow the same template: sort by end time, scan left to right, pick or skip. Master this family and you cover a huge chunk of greedy interview questions.