Language Selection
Choose your preferred programming language
Candy
Problem Statement
There are n children standing in a line. Each child is assigned a rating value given in the integer array ratings.
You are giving candies to these children subjected to the following requirements:
- Each child must have at least one candy.
- Children with a higher rating than their neighbor must get more candies than that neighbor.
Return the minimum number of candies you need to have to distribute the candies to the children.
Constraints:
n == ratings.length1 <= n <= 2 * 10^40 <= ratings[i] <= 2 * 10^4
Examples:
Example 1:
Input: ratings = [1,0,2]
Output: 5
Explanation: You can allocate [2,1,2] candies to the first, second, and third child respectively.
Example 2:
Input: ratings = [1,2,2]
Output: 4
Explanation: You can allocate [1,2,1] candies. The third child gets 1 candy because the requirement
only applies when a child has a strictly higher rating than a neighbor.
Example 3:
Input: ratings = [1,3,2,2,1]
Output: 7
Explanation: You can allocate [1,2,1,2,1] candies.
Example 4:
Input: ratings = [1,2,3,4,5]
Output: 15
Explanation: Strictly increasing ratings: [1,2,3,4,5] candies.
Approach 1: Brute Force (Iterative Adjustment)
Algorithm:
- Give every child 1 candy initially
- Repeatedly scan the array and fix violations (where a higher-rated child has fewer or equal candies compared to a neighbor)
- Continue until no more violations exist
- This converges because each adjustment only increases candy counts
Time Complexity: O(n^2) in the worst case Space Complexity: O(n)
Python:
def candy(ratings):
"""
Distribute minimum candies using iterative adjustment
Time: O(n^2)
Space: O(n)
"""
n = len(ratings)
candies = [1] * n
changed = True
while changed:
changed = False
for i in range(n):
# Check left neighbor
if i > 0 and ratings[i] > ratings[i - 1] and candies[i] <= candies[i - 1]:
candies[i] = candies[i - 1] + 1
changed = True
# Check right neighbor
if i < n - 1 and ratings[i] > ratings[i + 1] and candies[i] <= candies[i + 1]:
candies[i] = candies[i + 1] + 1
changed = True
return sum(candies)
Java:
class Solution {
/**
* Distribute minimum candies using iterative adjustment
* Time: O(n^2)
* Space: O(n)
*/
public int candy(int[] ratings) {
int n = ratings.length;
int[] candies = new int[n];
java.util.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;
}
}
Go:
// candy - Distribute minimum candies using iterative adjustment
// Time: O(n^2)
// Space: O(n)
func candy(ratings []int) int {
n := len(ratings)
candies := make([]int, n)
for i := range candies {
candies[i] = 1
}
changed := true
for changed {
changed = false
for 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
}
}
}
total := 0
for _, c := range candies {
total += c
}
return total
}
JavaScript:
/**
* Distribute minimum candies using iterative adjustment
* Time: O(n^2)
* Space: O(n)
*/
function candy(ratings) {
const n = ratings.length;
const candies = new Array(n).fill(1);
let changed = true;
while (changed) {
changed = false;
for (let 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;
}
}
}
return candies.reduce((sum, c) => sum + c, 0);
}
C#:
using System;
using System.Linq;
public class Solution {
/// <summary>
/// Distribute minimum candies using iterative adjustment
/// Time: O(n^2)
/// Space: O(n)
/// </summary>
public int Candy(int[] ratings) {
int n = ratings.Length;
int[] candies = new int[n];
Array.Fill(candies, 1);
bool 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;
}
}
}
return candies.Sum();
}
}
Approach 2: Two-Pass Greedy (Optimal)
Algorithm:
- Initialize every child with 1 candy
- Left-to-right pass: If
ratings[i] > ratings[i-1], setcandies[i] = candies[i-1] + 1(satisfy left neighbor constraint) - Right-to-left pass: If
ratings[i] > ratings[i+1], setcandies[i] = max(candies[i], candies[i+1] + 1)(satisfy right neighbor constraint while preserving left constraint) - Sum all candies for the answer
Time Complexity: O(n) Space Complexity: O(n)
Python:
def candy(ratings):
"""
Distribute minimum candies using two-pass greedy
Time: O(n)
Space: O(n)
"""
n = len(ratings)
if n <= 1:
return n
candies = [1] * n
# Left-to-right: satisfy left neighbor constraint
for i in range(1, n):
if ratings[i] > ratings[i - 1]:
candies[i] = candies[i - 1] + 1
# Right-to-left: satisfy right neighbor constraint
for i in range(n - 2, -1, -1):
if ratings[i] > ratings[i + 1]:
candies[i] = max(candies[i], candies[i + 1] + 1)
return sum(candies)
Java:
class Solution {
/**
* Distribute minimum candies using two-pass greedy
* Time: O(n)
* Space: O(n)
*/
public int candy(int[] ratings) {
int n = ratings.length;
if (n <= 1) return n;
int[] candies = new int[n];
java.util.Arrays.fill(candies, 1);
// Left-to-right: satisfy left neighbor constraint
for (int i = 1; i < n; i++) {
if (ratings[i] > ratings[i - 1]) {
candies[i] = candies[i - 1] + 1;
}
}
// Right-to-left: satisfy right neighbor constraint
for (int i = n - 2; i >= 0; i--) {
if (ratings[i] > ratings[i + 1]) {
candies[i] = Math.max(candies[i], candies[i + 1] + 1);
}
}
int total = 0;
for (int c : candies) {
total += c;
}
return total;
}
}
Go:
// candy - Distribute minimum candies using two-pass greedy
// Time: O(n)
// Space: O(n)
func candy(ratings []int) int {
n := len(ratings)
if n <= 1 {
return n
}
candies := make([]int, n)
for i := range candies {
candies[i] = 1
}
// Left-to-right: satisfy left neighbor constraint
for i := 1; i < n; i++ {
if ratings[i] > ratings[i-1] {
candies[i] = candies[i-1] + 1
}
}
// Right-to-left: satisfy right neighbor constraint
for i := n - 2; i >= 0; i-- {
if ratings[i] > ratings[i+1] {
if candies[i+1]+1 > candies[i] {
candies[i] = candies[i+1] + 1
}
}
}
total := 0
for _, c := range candies {
total += c
}
return total
}
JavaScript:
/**
* Distribute minimum candies using two-pass greedy
* Time: O(n)
* Space: O(n)
*/
function candy(ratings) {
const n = ratings.length;
if (n <= 1) return n;
const candies = new Array(n).fill(1);
// Left-to-right: satisfy left neighbor constraint
for (let i = 1; i < n; i++) {
if (ratings[i] > ratings[i - 1]) {
candies[i] = candies[i - 1] + 1;
}
}
// Right-to-left: satisfy right neighbor constraint
for (let i = n - 2; i >= 0; i--) {
if (ratings[i] > ratings[i + 1]) {
candies[i] = Math.max(candies[i], candies[i + 1] + 1);
}
}
return candies.reduce((sum, c) => sum + c, 0);
}
C#:
using System;
using System.Linq;
public class Solution {
/// <summary>
/// Distribute minimum candies using two-pass greedy
/// Time: O(n)
/// Space: O(n)
/// </summary>
public int Candy(int[] ratings) {
int n = ratings.Length;
if (n <= 1) return n;
int[] candies = new int[n];
Array.Fill(candies, 1);
// Left-to-right: satisfy left neighbor constraint
for (int i = 1; i < n; i++) {
if (ratings[i] > ratings[i - 1]) {
candies[i] = candies[i - 1] + 1;
}
}
// Right-to-left: satisfy right neighbor constraint
for (int i = n - 2; i >= 0; i--) {
if (ratings[i] > ratings[i + 1]) {
candies[i] = Math.Max(candies[i], candies[i + 1] + 1);
}
}
return candies.Sum();
}
}
Approach 3: Single Pass with Slopes
Algorithm:
- Traverse the array once, tracking ascending and descending slopes
- When going up (ascending slope), increment candy count for each step
- When going down (descending slope), track the length of the descent
- At the bottom of each descent, calculate candies for the entire descending segment using the formula
length * (length + 1) / 2 - Adjust the peak if the descending slope is longer than the ascending slope
Time Complexity: O(n) Space Complexity: O(1)
Python:
def candy(ratings):
"""
Distribute minimum candies using single-pass slope counting
Time: O(n)
Space: O(1)
"""
n = len(ratings)
if n <= 1:
return n
total = 1
up = 0
down = 0
peak = 0
for i in range(1, n):
if ratings[i] > ratings[i - 1]:
# Ascending
up += 1
down = 0
peak = up
total += up + 1 # current child gets (up + 1) candies
elif ratings[i] < ratings[i - 1]:
# Descending
up = 0
down += 1
# Add candies for descending slope
total += down
# If descending slope exceeds the peak, the peak needs one more
if down > peak:
total += 1
else:
# Equal ratings: reset both slopes
up = 0
down = 0
peak = 0
total += 1 # current child gets 1 candy
return total
Java:
class Solution {
/**
* Distribute minimum candies using single-pass slope counting
* Time: O(n)
* Space: O(1)
*/
public int candy(int[] ratings) {
int n = ratings.length;
if (n <= 1) return n;
int total = 1;
int up = 0;
int down = 0;
int peak = 0;
for (int i = 1; i < n; i++) {
if (ratings[i] > ratings[i - 1]) {
up++;
down = 0;
peak = up;
total += up + 1;
} else if (ratings[i] < ratings[i - 1]) {
up = 0;
down++;
total += down;
if (down > peak) {
total++;
}
} else {
up = 0;
down = 0;
peak = 0;
total++;
}
}
return total;
}
}
Go:
// candy - Distribute minimum candies using single-pass slope counting
// Time: O(n)
// Space: O(1)
func candy(ratings []int) int {
n := len(ratings)
if n <= 1 {
return n
}
total := 1
up := 0
down := 0
peak := 0
for i := 1; i < n; i++ {
if ratings[i] > ratings[i-1] {
up++
down = 0
peak = up
total += up + 1
} else if ratings[i] < ratings[i-1] {
up = 0
down++
total += down
if down > peak {
total++
}
} else {
up = 0
down = 0
peak = 0
total++
}
}
return total
}
JavaScript:
/**
* Distribute minimum candies using single-pass slope counting
* Time: O(n)
* Space: O(1)
*/
function candy(ratings) {
const n = ratings.length;
if (n <= 1) return n;
let total = 1;
let up = 0;
let down = 0;
let peak = 0;
for (let i = 1; i < n; i++) {
if (ratings[i] > ratings[i - 1]) {
up++;
down = 0;
peak = up;
total += up + 1;
} else if (ratings[i] < ratings[i - 1]) {
up = 0;
down++;
total += down;
if (down > peak) {
total++;
}
} else {
up = 0;
down = 0;
peak = 0;
total++;
}
}
return total;
}
C#:
public class Solution {
/// <summary>
/// Distribute minimum candies using single-pass slope counting
/// Time: O(n)
/// Space: O(1)
/// </summary>
public int Candy(int[] ratings) {
int n = ratings.Length;
if (n <= 1) return n;
int total = 1;
int up = 0;
int down = 0;
int peak = 0;
for (int i = 1; i < n; i++) {
if (ratings[i] > ratings[i - 1]) {
up++;
down = 0;
peak = up;
total += up + 1;
} else if (ratings[i] < ratings[i - 1]) {
up = 0;
down++;
total += down;
if (down > peak) {
total++;
}
} else {
up = 0;
down = 0;
peak = 0;
total++;
}
}
return total;
}
}
Key Insights
Two-Pass Decomposition: The left-to-right pass ensures every child with a higher rating than their left neighbor gets more candies. The right-to-left pass ensures the same for the right neighbor. Taking the maximum at each position satisfies both constraints simultaneously.
Why
max()in the Second Pass: Usingmax(candies[i], candies[i+1] + 1)in the right-to-left pass preserves the constraint established in the first pass. Simply assigningcandies[i+1] + 1could violate the left-neighbor constraint.Strictly Greater Only: Equal ratings do not require more candies. The child with rating
[1,2,2]gets[1,2,1]candies, not[1,2,2].Slope Counting Insight: The single-pass approach views the ratings as a sequence of ascending and descending slopes. The candy count for a slope of length
kis1 + 2 + ... + k = k*(k+1)/2. The peak between slopes gets the maximum of both slopes’ requirements.Minimum is Achievable: The two-pass greedy always produces the minimum total because each child gets exactly the minimum candies needed to satisfy both neighbors.
Edge Cases
- Single child:
[5]→1 - All equal ratings:
[3,3,3,3]→4(each child gets 1) - Strictly increasing:
[1,2,3,4,5]→15(candies: 1+2+3+4+5) - Strictly decreasing:
[5,4,3,2,1]→15(candies: 5+4+3+2+1) - V-shaped:
[3,2,1,2,3]→9(candies: 3+2+1+2+3) - Mountain:
[1,2,3,2,1]→9(candies: 1+2+3+2+1) - Plateau:
[1,2,2,2,1]→7(candies: 1+2+1+2+1) - Two elements:
[1,2]→3(candies: 1+2)
Test Cases
# Test case 1: Simple valley
assert candy([1,0,2]) == 5
# Test case 2: Equal neighbors
assert candy([1,2,2]) == 4
# Test case 3: Strictly increasing
assert candy([1,2,3,4,5]) == 15
# Test case 4: Strictly decreasing
assert candy([5,4,3,2,1]) == 15
# Test case 5: Single child
assert candy([5]) == 1
# Test case 6: All equal
assert candy([3,3,3,3]) == 4
# Test case 7: V-shape
assert candy([3,2,1,2,3]) == 9
# Test case 8: Mountain shape
assert candy([1,2,3,2,1]) == 9
# Test case 9: Complex pattern
assert candy([1,3,2,2,1]) == 7
# Test case 10: Two elements, decreasing
assert candy([2,1]) == 3
Common Mistakes
Treating equal ratings as “greater”: The constraint is strictly greater. Children with equal ratings are independent and can each receive 1 candy.
Using assignment instead of
maxin the second pass: Writingcandies[i] = candies[i+1] + 1instead ofcandies[i] = max(candies[i], candies[i+1] + 1)breaks the left-neighbor constraint established in the first pass.Only checking one direction: A single pass (left-to-right only) misses the right-neighbor constraint. For example,
[5,4,3,2,1]would incorrectly yield[1,1,1,1,1]with only a left-to-right pass.Off-by-one in the second pass: The right-to-left pass must iterate from
n - 2down to0(inclusive). Starting fromn - 1is harmless but wastes a step.Forgetting the base case: For a single child, the answer is 1. For an empty array, the answer is 0. Both should be handled explicitly.