LeetCode #135 Hard

Candy

Candy is LeetCode 135 (Hard). n children stand in a line, and ratings[i] is the rating of child i. Hand out candies so that:

  • every child gets at least one candy;
  • a child with a higher rating than an adjacent child gets more candies than that neighbour.

Return the minimum total number of candies. Equal neighbours set no rule between them. With up to 2 · 10⁴ children, the answer has to come from a few linear passes.

Constraints
  • n == ratings.length
  • 1 <= n <= 2 * 10⁴
  • 0 <= ratings[i] <= 2 * 10⁴
greedyarray
Open on LeetCode ↗
02

Intuition

Each child has two rules: one with the left neighbour and one with the right neighbour. Candy LeetCode is rated Hard because a single sweep cannot keep both: raising one child can break the rule with a child you already passed.

So deal with one rule at a time:

  • First pass, left to right: count candies using only the left-neighbour rule.
  • Second pass, right to left: count candies using only the right-neighbour rule.
  • Combine: each child gets the larger of its two counts. That keeps both rules true with as few candies as possible.
How to spot this pattern

Use this when every position has a constraint against both neighbours: one sweep in each direction settles each side, and a max (or min) merges them. Trapping Rain Water uses the same idea with the tallest bar to the left and to the right, and Product of Array Except Self combines a prefix pass with a suffix pass.

03

Approach

Try it first

Before reading on: for ratings = [1,2,5,4,3,2], give every child the fewest candies a left-to-right sweep allows. Which children break the rule against their right neighbour, and why can a second left-to-right sweep not fix the peak at 5?

1

Start everyone at one candy

Fill left and right with 1. That is the minimum every child must get, and it is also the right value for any child that no rule pushes higher.

2

Left to right: the left-neighbour rule

For i from 1 up: if ratings[i] > ratings[i - 1], set left[i] = left[i - 1] + 1. The left neighbour's count is already settled when you reach i, so one more than it is exactly enough. Equal or lower ratings keep 1.

3

Right to left: the right-neighbour rule

For i from n - 2 down: if ratings[i] > ratings[i + 1], set right[i] = right[i + 1] + 1. Go right to left because a falling run such as 5, 4, 3 has to be counted from its low end, and a left-to-right sweep reaches the high end first.

4

Take the max for each child

Child i gets max(left[i], right[i]), and the answer is the sum. The larger value satisfies both rules at once, and anything smaller breaks one of them, so the total is the minimum. Three passes give O(n) time and O(n) space.

04

Candy solution in Python | C++ | Java

▶1class Solution:
▶2 def candy(self, ratings: List[int]) -> int:
▶3 n = len(ratings)
▶4 left = [1] * n
▶5 right = [1] * n
▶6 for i in range(1, n):
▶7 if ratings[i] > ratings[i - 1]:
▶8 left[i] = left[i - 1] + 1
▶9 for i in range(n - 2, -1, -1):
▶10 if ratings[i] > ratings[i + 1]:
▶11 right[i] = right[i + 1] + 1
▶12 total = 0
▶13 for i in range(n):
▶14 total += max(left[i], right[i])
▶15 return total
ratings102012left111right111candiestotalevery child starts with 1 candy
left[1, 1, 1]rule against the left neighbour
right[1, 1, 1]rule against the right neighbour
Start at the minimum. Every child must get at least one candy, so both arrays start at 1. Each child has two rules, one per neighbour; the left array will satisfy the first and the right array the second.
ratings102012ileft111right111candiestotal0 ≤ 1 → left[1] stays 1
ratings1 → 0child 0 then child 1
left[1]1no rule to satisfy
Child 1 is not rated higher than its left neighbour, so the left rule asks nothing. It keeps the minimum, 1.
ratings102012ileft112right111candiestotal2 > 0 → left[2] = 2
ratings0 → 2child 1 then child 2
left[2]2one more than left[1]
Child 2 is rated higher than the child on its left, so it needs one more than that child: 2. Going left to right, left[1] is already final, so this is safe.
ratings102012ileft112right111candiestotal0 ≤ 2 → right[1] stays 1
ratings0 ← 2child 1 vs child 2
right[1]1no rule to satisfy
Child 1 is not rated higher than its right neighbour, so the right rule asks nothing and it stays at 1.
ratings102012ileft112right211candiestotal1 > 0 → right[0] = 2
ratings1 ← 0child 0 vs child 1
right[0]2one more than right[1]
Child 0 is rated higher than the child on its right, so it needs one more: 2. This pass runs right to left so that right[1] is final first; a falling run like 5, 4, 3 can only be counted from its low end.
ratings102012ileft112right211candies2total2child 0: max(1, 2) = 2, total 2
candies[0]2set by the right rule
total2running sum
Child 0 needs 1 for its left rule and 2 for its right rule. The larger, 2, satisfies both, and anything smaller would break the right rule.
ratings102012ileft112right211candies21total3child 1: max(1, 1) = 1, total 3
candies[1]1both rules agree
total3running sum
Both rules ask for 1 here, so child 1 gets 1.
ratings102012ileft112right211candies212total5child 2: max(2, 1) = 2, total 5
candies[2]2set by the left rule
total5running sum
Child 2 needs 2 for its left rule and 1 for its right rule. The larger, 2, satisfies both, and anything smaller would break the left rule.
ratings102012left112right211candies212total5return 5
candies[2, 1, 2]
result5minimum total
Answer 5. Each child got the smallest count that satisfies both neighbours, so no candy can be taken away without breaking a rule. Three linear passes: O(n) time.
05

One pass over slopes

up is the length of the current rising run and down the length of the current falling run. A rising child gets up + 1 candies. Each new child on a fall adds down, because everyone already on that fall needs one more; the peak gets one extra only when the fall grows longer than the rise.

▶1class Solution:
▶2 def candy(self, ratings: List[int]) -> int:
▶3 total, up, down, peak = 1, 0, 0, 0
▶4 for i in range(1, len(ratings)):
▶5 if ratings[i] > ratings[i - 1]:
▶6 up += 1
▶7 peak = up
▶8 down = 0
▶9 total += up + 1
▶10 elif ratings[i] == ratings[i - 1]:
▶11 up = down = peak = 0
▶12 total += 1
▶13 else:
▶14 up = 0
▶15 down += 1
▶16 total += down + (1 if down > peak else 0)
▶17 return total
06

Common pitfalls

Using only a left-to-right pass

✗ Wrong
for i in range(1, n):
    if ratings[i] > ratings[i - 1]:
        c[i] = c[i - 1] + 1
✓ Right
# left pass, right pass, then max per child

A forward sweep only compares each child with the one before it. For [3, 2, 1] it gives everyone 1, but child 0 needs 3 and child 1 needs 2; the answer is 6, not 3.

Giving equal ratings more candy

✗ Wrong
if ratings[i] >= ratings[i - 1]:
✓ Right
if ratings[i] > ratings[i - 1]:

Only a strictly higher rating sets a rule. For [1, 2, 2] the last child may have 1 candy, fewer than its equal neighbour: the answer is 4, and >= returns 6.

Adding the two passes instead of taking the max

✗ Wrong
total += left[i] + right[i]
✓ Right
total += max(left[i], right[i])

Each child gets one count that must satisfy both rules, and the larger already satisfies the smaller. Adding them counts every child's base candy twice; [1, 0, 2] gives 8 instead of 5.

07

Edge cases

All ratings equal

No rating is strictly higher than a neighbour, so everyone keeps 1 and the answer is n.

Strictly decreasing ratings

Only the right-to-left pass raises anything: the counts run n, n − 1, …, 1, totalling n(n + 1) / 2.

08

Complexity

Time
O(n)
Space
O(n)
Three linear passes over two arrays of length n. The slope method below gets the space down to O(1).
09

Candy FAQ

Why is LeetCode 135 a greedy problem?

Each pass makes the smallest choice the rule allows, one more than the neighbour or else 1, and never revisits it. Taking the max of two minimal answers is still minimal, so the greedy choices add up to the smallest total.

Can the two arrays share one array?

Yes. Run the left pass into candies, then in the right pass set candies[i] = max(candies[i], candies[i + 1] + 1). The max is essential: a plain assignment would erase what the left pass found.