Candy

HardGreedy · Two Passes🧩 GreedyLeetCode #135 ↗Updated

Problem

Children stand in a row and child i has score ratings[i]. Hand out candies so that every child gets at least one, and any child with a strictly higher score than a direct neighbour gets more candies than that neighbour. Return the smallest total number of candies needed.
Inputratings = [1,0,2]Output5 (2,1,2)
Inputratings = [1,2,2]Output4 (1,2,1)
Inputratings = [1,3,4,2,1]Output9 (1,2,3,2,1)

1 ≤ n ≤ 2·104  ·  0 ≤ ratings[i] ≤ 2·104

Try it yourself

Java
class Solution {
    public int candy(int[] ratings) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
Split the rule into two one-sided rules: a left-to-right pass satisfies “beat your left neighbour”, a right-to-left pass satisfies “beat your right neighbour”. Each child needs the max of the two — or, counting slopes, every rising and falling run is just 1+2+…+len.
PatternGreedy

Instinct An optimisation where one obvious local rule (earliest end, farthest reach, biggest first) never needs undoing - commit to it, then try hard to break it.

Memory hack Grab the best bite now and never look back - but only once a counterexample hunt fails to make you regret it.

Open the Greedy guide →

Approaches

Brute ForceRelax Until Stable

Time O(n2)Space O(n)

Give everyone 1 candy, then sweep the row repeatedly: whenever a child out-scores a neighbour but doesn't have more candy, bump them to neighbour + 1. Values only ever rise to the minimum they are forced to, so when a sweep changes nothing the total is minimal. A long descending run can need about n sweeps.

Java
public int candy(int[] ratings) {
    int n = ratings.length;
    int[] candies = new int[n];
    Arrays.fill(candies, 1);
    boolean changed = true;
    while (changed) {
        changed = false;
        for (int i = 0; i < n; i++) {
            if (i > 0 && ratings[i] > ratings[i - 1] && candies[i] <= candies[i - 1]) {
                candies[i] = candies[i - 1] + 1;
                changed = true;
            }
            if (i < n - 1 && ratings[i] > ratings[i + 1] && candies[i] <= candies[i + 1]) {
                candies[i] = candies[i + 1] + 1;
                changed = true;
            }
        }
    }
    int total = 0;
    for (int c : candies) total += c;
    return total;
}

Obviously correct, but quadratic on long monotone runs.

At a glance

LevelApproachTimeSpace
Brute ForceRelax Until StableO(n2)O(n)
BetterTwo Passes (Left & Right Arrays)O(n)O(n)
OptimalSingle Pass Slope CountingO(n)O(1)
Common pitfalls
  • Giving equal-rated neighbours equal candy is not required — equal ratings impose no rule.
  • Using one pass only — a single direction cannot see a longer run on the other side.
  • In the slope method, forgetting the peak adjustment double-counts or under-counts the top.
Interview tips
  • Decompose the two-sided rule into two one-sided rules — that's the real insight.
  • Prove minimality: each child gets exactly the length of its longest forced chain.
  • Offer the O(1) slope version only after the two-pass solution is solid.
Pattern
Two-directional greedy pass → a constraint depends on both neighbours: satisfy each side separately, then combine.
My notes saved in this browser