Distribute Candies to People: Round-Robin Simulation
You have candies candies to distribute to num_people people standing in a row. Starting from the first person, you give 1 candy to the first person, 2 to the second, 3 to the third, and so on, cycling back to the first person after the last. Each turn, the amount increases by one. If you do not have enough candies to give the full amount on a turn, you give whatever remains. Return an array showing how many candies each person ends up with.
This is LeetCode 1103: Distribute Candies to People.
Why this problem matters
Distribute Candies to People is a clean introduction to round-robin simulation. You are iterating through a group of people in a cycle, giving each person an increasing amount, and handling the boundary case when you run out. This pattern of cycling through indices with modular arithmetic shows up everywhere: task scheduling, circular buffers, and load balancing.
The problem is rated easy, but it teaches you to think carefully about loop termination. The number of turns is not fixed. You keep going until the candies run out. Knowing when to stop and how to handle the final partial amount is a skill that transfers to many harder problems.
It also gives you practice with a common interview building block: mapping a linear counter to a circular index using the modulo operator. Once you internalize i % n, you can apply it to any problem that cycles through a fixed-size group.
The key insight
You do not need to think in terms of "rounds" at all. Instead, maintain a single counter give that starts at 1 and increments each turn. On each turn, determine which person receives candies using turn % num_people. Give them min(give, candies_remaining). This flattens the round-robin into a single linear loop that terminates when candies hits zero.
The modular index turn % num_people handles the wrap-around automatically. You never need to write nested loops for "round 1, round 2, round 3." One loop, one counter, one modulo.
The solution
def distributeCandies(candies, num_people):
result = [0] * num_people
give = 1
while candies > 0:
result[(give - 1) % num_people] += min(give, candies)
candies -= min(give, candies)
give += 1
return result
The result array accumulates each person's total. On each iteration, (give - 1) % num_people maps the turn number (1-indexed) to a person index (0-indexed). We add min(give, candies) because on the last turn, there may be fewer candies than the scheduled amount. After giving, we subtract the same amount and increment the turn counter.
The loop terminates naturally when candies reaches zero. There is no need to precompute how many rounds or turns there will be.
The key to clean simulation code is reducing nested structures to a single loop. Instead of looping over rounds and then over people within each round, use modular arithmetic to flatten everything into one sequence of turns.
Visual walkthrough
Step 1: Give person 1 one candy (turn = 1)
We start with 7 candies. Turn 1 gives min(1, 7) = 1 candy to person 1. Remaining: 7 - 1 = 6.
result = [1, 0, 0, 0], candies left = 6
Step 2: Give person 2 two candies (turn = 2)
Turn 2 gives min(2, 6) = 2 candies to person 2. Remaining: 6 - 2 = 4.
result = [1, 2, 0, 0], candies left = 4
Step 3: Give person 3 three candies (turn = 3)
Turn 3 gives min(3, 4) = 3 candies to person 3. Remaining: 4 - 3 = 1.
result = [1, 2, 3, 0], candies left = 1
Step 4: Give person 4 the remainder (turn = 4)
Turn 4 would give 4, but only 1 candy remains. Give min(4, 1) = 1. Remaining: 0.
result = [1, 2, 3, 1], candies left = 0. Done!
With candies = 7 and num_people = 4, the simulation runs exactly 4 turns. Turns 1 through 3 give the full scheduled amount (1, 2, 3). Turn 4 would give 4, but only 1 candy remains, so person 4 gets the leftover. The final distribution is [1, 2, 3, 1].
Complexity analysis
| Approach | Time | Space |
|---|---|---|
| Simulation | O(sqrt(candies)) | O(n) |
Time: Each turn gives away at least give candies, and give increases by 1 each turn. The total candies distributed after k turns is 1 + 2 + ... + k = k(k+1)/2. Setting this equal to candies and solving for k gives roughly k = O(sqrt(candies)). So the loop runs O(sqrt(candies)) times.
Space: The result array has num_people entries, so space is O(n) where n is num_people. No additional data structures are needed.
The building blocks
1. Round-robin with modular indexing
The pattern of cycling through a fixed-size group using the modulo operator:
for turn in range(total_turns):
person = turn % num_people
do_something(person)
In this problem, (give - 1) % num_people maps a 1-indexed turn counter to a 0-indexed person. The same pattern appears in Josephus problems, circular queue implementations, and any scenario where you wrap around to the beginning after reaching the end. The modulo operator eliminates the need for explicit "if index equals length, reset to zero" checks.
2. Handling the remainder
The pattern of capping a value at the remaining budget:
actual = min(scheduled, remaining)
remaining -= actual
In this problem, min(give, candies) ensures you never distribute more than you have. This same guard appears in problems like splitting arrays, distributing tasks with capacity limits, and any simulation where a resource can be exhausted partway through a step.
Edge cases
- Exactly enough candies for one full round:
candies = 10, num_people = 4gives[1, 2, 3, 4]with 0 left over. The loop ends cleanly after 4 turns. - Fewer candies than the first turn needs:
candies = 0, num_people = 3gives[0, 0, 0]. The loop body never executes. - One candy:
candies = 1, num_people = 5gives[1, 0, 0, 0, 0]. Only the first person gets anything. - One person:
candies = 10, num_people = 1gives[10]. Every turn goes to the same person, so they get everything. - Multiple rounds:
candies = 30, num_people = 3wraps around multiple times. Person 0 gets turns 1, 4, 7 (amounts 1 + 4 + 7 = 12). Person 1 gets turns 2, 5, 8 (amounts 2 + 5 + 2 = 9). Person 2 gets turns 3, 6 (amounts 3 + 6 = 9). Final:[12, 9, 9].
From understanding to recall
You have read the simulation loop and it makes sense. One counter, one modulo, one min. But when you sit down in an interview and someone says "distribute increasing amounts in a round-robin cycle," will the code flow out automatically?
The details trip people up: using (give - 1) % num_people instead of give % num_people, remembering to subtract the actual amount given (not the scheduled amount), and handling the final partial turn correctly. These are small points, but getting any one of them wrong means a failing test case.
Spaced repetition locks these details in. You write the simulation from scratch, check it, and repeat at increasing intervals. After a few sessions, the round-robin modular indexing pattern becomes muscle memory. You stop thinking about off-by-one errors because you have already made and corrected them multiple times.
Related posts
- Fizz Buzz - Another simulation-style problem where you iterate through numbers and apply conditional rules at each step
- Distribute Candies - A different candy distribution problem using sorting and set-based counting
- Assign Cookies - Greedy distribution where you match resources to recipients in sorted order
CodeBricks breaks the distribute candies to people LeetCode problem into its round-robin indexing and remainder handling building blocks, then drills them independently with spaced repetition. You type each piece from scratch until the pattern is automatic. When a simulation question shows up in your interview, the modular arithmetic and boundary handling are already second nature.