575. Distribute Candies
Problem Description
Alice has n
candies where each candy has a type represented by candyType[i]
. Since Alice is concerned about her weight, she consulted a doctor who advised her to eat only n/2
of her candies (where n
is always even).
Alice wants to maximize the variety of her candy consumption - she wants to eat as many different types of candies as possible while following the doctor's restriction of eating only half of her total candies.
Given an integer array candyType
of length n
, you need to find the maximum number of different candy types Alice can eat if she consumes exactly n/2
candies.
For example, if Alice has 6 candies of types [1, 1, 2, 2, 3, 3]
, she can eat 3 candies (which is 6/2
). The best strategy would be to eat one candy of each type [1, 2, 3]
, giving her 3 different types. Even though she has 3 unique types available and is allowed to eat 3 candies, she achieves the maximum variety of 3 different types.
The solution uses a set to count unique candy types. The answer is the minimum between:
n/2
(the maximum number of candies she can eat)- The total number of unique candy types available
This ensures Alice eats the maximum variety possible while respecting the doctor's limit.
Intuition
To maximize the variety of candies Alice eats, she should eat as many different types as possible. Since she can only eat n/2
candies total, the optimal strategy is to eat one candy of each type until she either:
- Runs out of different types to try, or
- Reaches her limit of
n/2
candies
Let's think through the two scenarios:
Scenario 1: Few unique types
If there are fewer unique candy types than n/2
, Alice can eat one of each type. For instance, if she has 10 candies but only 3 unique types, and she can eat 5 candies, she'll eat all 3 different types (one of each) and still have 2 slots left. She'll have to repeat some types, but the maximum different types she can eat is still just 3.
Scenario 2: Many unique types
If there are more unique candy types than n/2
, Alice is limited by the doctor's restriction. For example, if she has 10 candies with 8 unique types, but can only eat 5 candies, the best she can do is pick 5 different types (one candy each).
This leads us to realize the answer is simply the minimum of:
- The number of unique candy types (found using
len(set(candyType))
) - The maximum candies she can eat (
n/2
orlen(candyType) >> 1
)
The min()
function naturally handles both scenarios - it gives us the unique types count when that's the limiting factor, or n/2
when the doctor's restriction is the limiting factor.
Solution Approach
The solution uses a hash table (implemented as a set in Python) to efficiently count the unique candy types. Here's how the implementation works:
Step 1: Count unique candy types
We convert the candyType
list into a set: set(candyType)
. This automatically removes all duplicates and gives us only the unique candy types. The time complexity for this operation is O(n)
where n
is the total number of candies.
Step 2: Calculate the doctor's limit
We need to find n/2
, which represents the maximum number of candies Alice can eat. The code uses bit shifting len(candyType) >> 1
which is equivalent to integer division by 2. Right shifting by 1 bit effectively divides the number by 2.
Step 3: Find the minimum
We take the minimum of:
len(candyType) >> 1
- the maximum candies Alice is allowed to eatlen(set(candyType))
- the total number of unique candy types available
The min()
function returns whichever value is smaller, which represents the maximum variety Alice can achieve.
Why this works:
- If unique types <
n/2
: Alice can eat one of each type, so the answer is the number of unique types - If unique types ≥
n/2
: Alice is limited by the doctor's restriction, so the answer isn/2
Time Complexity: O(n)
for creating the set
Space Complexity: O(n)
in worst case when all candies are of different types
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to illustrate the solution approach.
Example: candyType = [1, 1, 2, 3, 3, 3]
Step 1: Understand the constraints
- Alice has 6 candies total
- She can eat
n/2 = 6/2 = 3
candies - The candy types are: type 1 (appears 2 times), type 2 (appears 1 time), type 3 (appears 3 times)
Step 2: Count unique types
- Convert to set:
set([1, 1, 2, 3, 3, 3])
={1, 2, 3}
- Number of unique types = 3
Step 3: Apply the solution logic
- Maximum candies Alice can eat:
6 >> 1 = 3
- Number of unique candy types:
len({1, 2, 3}) = 3
- Answer:
min(3, 3) = 3
Why this makes sense: Alice has exactly 3 unique candy types and is allowed to eat 3 candies. The optimal strategy is to eat one candy of each type: one type-1 candy, one type-2 candy, and one type-3 candy. This gives her the maximum variety of 3 different types.
Another example to contrast: candyType = [1, 1, 1, 1, 2, 3]
- Alice can eat
6/2 = 3
candies - Unique types:
{1, 2, 3}
= 3 types - Answer:
min(3, 3) = 3
Even though type 1 appears 4 times, Alice still eats one of each type for maximum variety.
Edge case example: candyType = [1, 1, 1, 1, 1, 1]
- Alice can eat
6/2 = 3
candies - Unique types:
{1}
= 1 type - Answer:
min(3, 1) = 1
Here, even though Alice can eat 3 candies, she only has 1 type available, so the maximum variety is just 1.
Solution Implementation
1class Solution:
2 def distributeCandies(self, candyType: List[int]) -> int:
3 """
4 Determines the maximum number of different candy types that can be eaten
5 when limited to eating exactly n/2 candies.
6
7 Args:
8 candyType: List of integers where each integer represents a candy type
9
10 Returns:
11 Maximum number of different candy types that can be consumed
12 """
13 # Calculate half the total number of candies (using bit shift for efficiency)
14 # This represents the maximum number of candies that can be eaten
15 max_candies_allowed = len(candyType) >> 1
16
17 # Count the number of unique candy types available
18 unique_candy_types = len(set(candyType))
19
20 # Return the minimum between:
21 # 1. The number of candies we're allowed to eat (n/2)
22 # 2. The total number of unique candy types
23 # This ensures we don't exceed the eating limit while maximizing variety
24 return min(max_candies_allowed, unique_candy_types)
25
1class Solution {
2 /**
3 * Distributes candies between two people such that one person gets maximum variety.
4 * Given an array of candy types, one person can eat at most n/2 candies.
5 * Returns the maximum number of different candy types one person can eat.
6 *
7 * @param candyType Array where each element represents a candy type
8 * @return Maximum number of different candy types one person can eat
9 */
10 public int distributeCandies(int[] candyType) {
11 // Use a HashSet to store unique candy types
12 Set<Integer> uniqueCandyTypes = new HashSet<>();
13
14 // Add all candy types to the set (duplicates are automatically removed)
15 for (int candy : candyType) {
16 uniqueCandyTypes.add(candy);
17 }
18
19 // Calculate the maximum candies one person can eat (half of total candies)
20 int maxCandiesAllowed = candyType.length / 2;
21
22 // Return the minimum between:
23 // 1. Maximum candies allowed (n/2)
24 // 2. Number of unique candy types available
25 // This ensures we don't exceed the candy limit while maximizing variety
26 return Math.min(maxCandiesAllowed, uniqueCandyTypes.size());
27 }
28}
29
1class Solution {
2public:
3 int distributeCandies(vector<int>& candyType) {
4 // Create a set to store unique candy types
5 // This automatically removes duplicates from the candy array
6 unordered_set<int> uniqueCandyTypes(candyType.begin(), candyType.end());
7
8 // Calculate the maximum number of candies that can be eaten (half of total)
9 int maxCandiesAllowed = candyType.size() / 2;
10
11 // Return the minimum between:
12 // 1. Maximum candies allowed to eat (n/2)
13 // 2. Number of unique candy types available
14 // This ensures we don't exceed the eating limit while maximizing variety
15 return min(maxCandiesAllowed, static_cast<int>(uniqueCandyTypes.size()));
16 }
17};
18
1/**
2 * Distributes candies between Alice and Bob, where Alice wants to maximize
3 * the number of different candy types she can eat.
4 *
5 * @param candyType - Array of integers where each integer represents a candy type
6 * @returns Maximum number of different candy types Alice can eat
7 */
8function distributeCandies(candyType: number[]): number {
9 // Create a set to store unique candy types
10 const uniqueCandyTypes: Set<number> = new Set(candyType);
11
12 // Calculate the maximum number of candies Alice can eat (half of total)
13 const maxCandiesAliceCanEat: number = candyType.length >> 1; // Bitwise right shift for division by 2
14
15 // Return the minimum between unique candy types and half the total candies
16 // This ensures Alice doesn't eat more than allowed while maximizing variety
17 return Math.min(uniqueCandyTypes.size, maxCandiesAliceCanEat);
18}
19
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the candyType
list. This is because creating a set from the list requires iterating through all n
elements once to build the set, which takes O(n)
time. The len()
operations and the min()
comparison are both O(1)
operations, and the bit shift operation (>> 1
) is also O(1)
.
The space complexity is O(n)
in the worst case. This occurs when all candy types are unique, resulting in a set that contains all n
elements from the original list. The set creation requires additional space proportional to the number of unique elements, which can be up to n
in the worst case.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Integer Division Confusion
A common mistake is using regular division /
instead of integer division //
or bit shifting when calculating n/2
. This can lead to float values which cause errors when comparing with integer counts.
Incorrect approach:
max_candies_allowed = len(candyType) / 2 # Returns float (e.g., 3.0)
return min(max_candies_allowed, unique_candy_types) # Comparing float with int
Correct approach:
max_candies_allowed = len(candyType) // 2 # Integer division
# OR
max_candies_allowed = len(candyType) >> 1 # Bit shift (as in the solution)
2. Attempting to Track Which Specific Candies to Eat
Some developers overcomplicate the problem by trying to determine exactly which candies Alice should eat, creating unnecessary data structures to track selections.
Overcomplicated approach:
def distributeCandies(self, candyType: List[int]) -> int:
candy_count = {}
for candy in candyType:
candy_count[candy] = candy_count.get(candy, 0) + 1
selected = []
for candy_type in candy_count:
if len(selected) < len(candyType) // 2:
selected.append(candy_type)
return len(selected)
Simplified approach (as in the solution):
return min(len(candyType) >> 1, len(set(candyType)))
The problem only asks for the COUNT of different types, not which specific candies to eat.
3. Not Handling Edge Cases
While the problem states n
is always even, developers might forget to consider minimal inputs like an empty array or arrays with only one type of candy.
Missing edge case handling:
def distributeCandies(self, candyType: List[int]) -> int:
unique_types = set(candyType)
return len(unique_types) # Forgets the n/2 limit
This would incorrectly return 4 for input [1,2,3,4,4,4,4,4]
when the answer should be 4 (since 8/2 = 4).
4. Inefficient Unique Counting
Using nested loops or manual counting instead of leveraging set's built-in duplicate removal.
Inefficient approach:
def distributeCandies(self, candyType: List[int]) -> int:
unique_types = []
for candy in candyType:
if candy not in unique_types: # O(m) operation for each candy
unique_types.append(candy)
return min(len(candyType) // 2, len(unique_types))
This has O(n²) time complexity in worst case, while using set()
maintains O(n) complexity.
Consider the classic dynamic programming of fibonacci numbers, what is the recurrence relation?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!