Facebook Pixel

1282. Group the People Given the Group Size They Belong To

MediumGreedyArrayHash Table
Leetcode Link

Problem Description

You have n people numbered from 0 to n - 1, and you need to organize them into groups. Each person i has a required group size specified in groupSizes[i], which tells you the size of the group that person i must be placed in.

For example, if groupSizes[1] = 3, then person 1 must be placed in a group that contains exactly 3 people total.

Your task is to return a list of groups where:

  • Each person i is placed in a group of size groupSizes[i]
  • Every person appears in exactly one group
  • All people from 0 to n - 1 are assigned to a group

The solution uses a hash table approach where we first collect all people by their required group size. For instance, if multiple people need to be in groups of size 3, we collect all of them together. Then, we partition these collected people into actual groups of the required size.

The key insight is that if we have 6 people who all need to be in groups of size 3, we can form 2 groups of size 3. The code accomplishes this by:

  1. Grouping people by their required group size using a dictionary
  2. Splitting each collection into chunks of the appropriate size

Since multiple valid solutions may exist (the exact composition of groups can vary as long as size requirements are met), any valid grouping is acceptable.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key observation is that people with the same group size requirement can be grouped together. If multiple people need to be in groups of size k, we can collect them and then divide them into groups of exactly k people each.

Think of it like organizing a party where guests have specific table size preferences. If 6 people want to sit at tables of 3, we know we need exactly 2 tables of size 3. We don't need to worry about which specific people sit together - we just need to ensure each table has exactly 3 people.

This leads us to a two-step approach:

  1. Collect by requirement: First, gather all people who have the same group size requirement. If persons 0, 2, and 5 all need groups of size 3, we collect them together.

  2. Partition into groups: Once we have all people with the same requirement collected, we can split them into actual groups. If we collected 9 people who all need groups of size 3, we simply create 3 groups of 3 people each.

The beauty of this approach is that the problem guarantees a valid solution exists. This means if we have people requiring groups of size k, the total count of such people will always be divisible by k. For example, we'll never have 5 people wanting groups of size 3 - it will always be 3, 6, 9, etc.

By using a hash table (or array) indexed by group size, we can efficiently collect people, and then use list slicing to partition them into appropriately sized groups. The expression v[j : j + i] creates chunks of size i from the list v, where i is the required group size and v contains all people needing that group size.

Learn more about Greedy patterns.

Solution Approach

The solution uses a hash table (dictionary) to organize people by their required group sizes, then partitions them into actual groups.

Step 1: Group Collection Using Hash Table

We create a defaultdict(list) called g where:

  • The key is the group size requirement
  • The value is a list of people (their IDs) who need that group size
g = defaultdict(list)
for i, v in enumerate(groupSizes):
    g[v].append(i)

For example, if groupSizes = [3, 3, 3, 3, 3, 1, 3]:

  • g[3] will contain [0, 1, 2, 3, 4, 6] (6 people needing groups of size 3)
  • g[1] will contain [5] (1 person needing a group of size 1)

Step 2: Partition into Groups

Once we have all people organized by their required group size, we partition each list into chunks of the appropriate size:

return [v[j : j + i] for i, v in g.items() for j in range(0, len(v), i)]

This list comprehension does the following:

  • for i, v in g.items(): Iterate through each group size i and its list of people v
  • for j in range(0, len(v), i): Create starting indices j at intervals of i (0, i, 2i, 3i, ...)
  • v[j : j + i]: Extract a slice of exactly i people starting from index j

Using our example:

  • For g[3] = [0, 1, 2, 3, 4, 6] with size 3:
    • j = 0: Extract [0, 1, 2] (positions 0 to 2)
    • j = 3: Extract [3, 4, 6] (positions 3 to 5)
  • For g[1] = [5] with size 1:
    • j = 0: Extract [5] (position 0 to 0)

Alternative: Array-Based Approach

As mentioned in the reference, since n has a small range, we could use an array instead of a hash table:

g = [[] for _ in range(n + 1)]

This would be slightly more efficient as array access is faster than hash table operations, but the logic remains the same.

The time complexity is O(n) where we iterate through all people once to group them and once more to partition them. The space complexity is also O(n) for storing the intermediate groupings.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example with groupSizes = [2, 1, 3, 3, 3, 2].

We have 6 people (indexed 0 to 5) with the following requirements:

  • Person 0 needs a group of size 2
  • Person 1 needs a group of size 1
  • Person 2 needs a group of size 3
  • Person 3 needs a group of size 3
  • Person 4 needs a group of size 3
  • Person 5 needs a group of size 2

Step 1: Collect people by their required group size

We create a hash table to group people by their size requirement:

  • When we see person 0 needs size 2, we add 0 to g[2]
  • When we see person 1 needs size 1, we add 1 to g[1]
  • When we see person 2 needs size 3, we add 2 to g[3]
  • When we see person 3 needs size 3, we add 3 to g[3]
  • When we see person 4 needs size 3, we add 4 to g[3]
  • When we see person 5 needs size 2, we add 5 to g[2]

After this step, our hash table looks like:

g[1] = [1]
g[2] = [0, 5]
g[3] = [2, 3, 4]

Step 2: Partition each collection into groups of the required size

Now we split each list into chunks of the appropriate size:

  • For g[1] = [1] with required size 1:

    • We take slices of size 1: [1]
    • This forms one group: [1]
  • For g[2] = [0, 5] with required size 2:

    • We take slices of size 2: [0, 5]
    • This forms one group: [0, 5]
  • For g[3] = [2, 3, 4] with required size 3:

    • We take slices of size 3: [2, 3, 4]
    • This forms one group: [2, 3, 4]

Final Result: [[1], [0, 5], [2, 3, 4]]

Each person is placed in exactly one group, and each group has the size that all its members required. Person 0 and 5 both wanted groups of size 2 and ended up together in a group of 2. Persons 2, 3, and 4 all wanted groups of size 3 and formed a group of 3. Person 1 wanted a group of size 1 and got their own group.

Solution Implementation

1from collections import defaultdict
2from typing import List
3
4class Solution:
5    def groupThePeople(self, groupSizes: List[int]) -> List[List[int]]:
6        # Dictionary to store people grouped by their required group size
7        # Key: group size, Value: list of person indices
8        groups_by_size = defaultdict(list)
9      
10        # Iterate through each person and their required group size
11        for person_index, required_group_size in enumerate(groupSizes):
12            # Add person's index to the list of people requiring the same group size
13            groups_by_size[required_group_size].append(person_index)
14      
15        # Build the final result by splitting people into groups of required size
16        result = []
17        for group_size, people_indices in groups_by_size.items():
18            # Split the list of people into chunks of size 'group_size'
19            for start_index in range(0, len(people_indices), group_size):
20                # Extract a group of exactly 'group_size' people
21                group = people_indices[start_index:start_index + group_size]
22                result.append(group)
23      
24        return result
25
1class Solution {
2    public List<List<Integer>> groupThePeople(int[] groupSizes) {
3        int n = groupSizes.length;
4      
5        // Create an array of lists where index represents group size
6        // and value is a list of person indices who belong to that group size
7        List<Integer>[] groupsBySize = new List[n + 1];
8        Arrays.setAll(groupsBySize, index -> new ArrayList<>());
9      
10        // Populate the array: add each person's index to their corresponding group size
11        for (int personIndex = 0; personIndex < n; personIndex++) {
12            int size = groupSizes[personIndex];
13            groupsBySize[size].add(personIndex);
14        }
15      
16        // Result list to store all groups
17        List<List<Integer>> result = new ArrayList<>();
18      
19        // Process each group size
20        for (int groupSize = 0; groupSize < groupsBySize.length; groupSize++) {
21            List<Integer> peopleWithThisGroupSize = groupsBySize[groupSize];
22          
23            // Split people with same group size into actual groups
24            // Each group will have exactly 'groupSize' number of people
25            for (int startIndex = 0; startIndex < peopleWithThisGroupSize.size(); startIndex += groupSize) {
26                // Add a sublist of size 'groupSize' to the result
27                result.add(peopleWithThisGroupSize.subList(startIndex, startIndex + groupSize));
28            }
29        }
30      
31        return result;
32    }
33}
34
1class Solution {
2public:
3    vector<vector<int>> groupThePeople(vector<int>& groupSizes) {
4        int n = groupSizes.size();
5      
6        // Create buckets where index represents group size
7        // Each bucket stores indices of people who belong to groups of that size
8        vector<vector<int>> buckets(n + 1);
9      
10        // Populate buckets: person at index i belongs to a group of size groupSizes[i]
11        for (int i = 0; i < n; ++i) {
12            buckets[groupSizes[i]].push_back(i);
13        }
14      
15        vector<vector<int>> result;
16      
17        // Process each bucket
18        for (int groupSize = 0; groupSize < buckets.size(); ++groupSize) {
19            // Skip empty buckets (groupSize 0 or no people for this group size)
20            if (buckets[groupSize].empty()) {
21                continue;
22            }
23          
24            // Split people in this bucket into groups of size 'groupSize'
25            for (int startIdx = 0; startIdx < buckets[groupSize].size(); startIdx += groupSize) {
26                // Create a group by taking 'groupSize' consecutive people from the bucket
27                vector<int> currentGroup(buckets[groupSize].begin() + startIdx, 
28                                        buckets[groupSize].begin() + startIdx + groupSize);
29                result.push_back(currentGroup);
30            }
31        }
32      
33        return result;
34    }
35};
36
1/**
2 * Groups people into groups based on their required group sizes
3 * @param groupSizes - Array where groupSizes[i] is the required group size for person i
4 * @returns Array of groups, where each group contains indices of people in that group
5 */
6function groupThePeople(groupSizes: number[]): number[][] {
7    const totalPeople: number = groupSizes.length;
8  
9    // Create buckets to store people indices by their required group size
10    // Index represents group size, value is array of person indices
11    const peopleBySizeRequirement: number[][] = Array.from(
12        { length: totalPeople + 1 }, 
13        () => []
14    );
15
16    // Distribute people into buckets based on their required group size
17    for (let personIndex = 0; personIndex < groupSizes.length; personIndex++) {
18        const requiredSize: number = groupSizes[personIndex];
19        peopleBySizeRequirement[requiredSize].push(personIndex);
20    }
21  
22    const resultGroups: number[][] = [];
23  
24    // Process each bucket and form groups of the corresponding size
25    for (let groupSize = 1; groupSize <= totalPeople; groupSize++) {
26        const peopleRequiringThisSize: number[] = peopleBySizeRequirement[groupSize];
27      
28        // Split people requiring same group size into actual groups
29        for (let startIndex = 0; startIndex < peopleRequiringThisSize.length; startIndex += groupSize) {
30            // Extract a group of exactly 'groupSize' people
31            const currentGroup: number[] = peopleRequiringThisSize.slice(
32                startIndex, 
33                startIndex + groupSize
34            );
35            resultGroups.push(currentGroup);
36        }
37    }
38  
39    return resultGroups;
40}
41

Time and Space Complexity

Time Complexity: O(n)

The algorithm consists of two main parts:

  1. Building the dictionary g by iterating through groupSizes once with enumerate(), which takes O(n) time where each append operation is O(1).
  2. Constructing the result list using a list comprehension that iterates through all dictionary items and slices the lists. Since each person (index) appears exactly once in the final result, the total number of elements processed across all slices is n, making this part also O(n).

Therefore, the overall time complexity is O(n) + O(n) = O(n).

Space Complexity: O(n)

The space usage includes:

  1. The dictionary g which stores all n indices from the input array, using O(n) space.
  2. The output list which must contain all n people distributed across various groups, also using O(n) space.

Since the output space is typically not counted against space complexity (as it's required for the answer), the auxiliary space complexity is O(n) for the dictionary. If we count the output space, the total space complexity remains O(n).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

Pitfall 1: Assuming Invalid Input or Inconsistent Group Sizes

The Problem: A common mistake is overthinking the problem and trying to handle cases where the total number of people requiring a specific group size isn't divisible by that group size. For instance, worrying about what to do if 5 people all require groups of size 3.

Why This Happens: Developers often add unnecessary validation or error handling code like:

if len(people_indices) % group_size != 0:
    # Handle error or raise exception
    raise ValueError("Cannot form complete groups")

The Solution: The problem guarantees that a valid solution always exists. If people require groups of a certain size, the input will always provide exactly the right number of people to form complete groups. Trust the problem constraints and avoid adding unnecessary complexity.

Pitfall 2: Modifying the Dictionary While Building Groups

The Problem: Some developers try to optimize by removing people from the dictionary as they form groups:

groups_by_size = defaultdict(list)
for i, v in enumerate(groupSizes):
    groups_by_size[v].append(i)
    # Trying to form groups immediately
    if len(groups_by_size[v]) == v:
        result.append(groups_by_size[v])
        groups_by_size[v] = []  # Clear the list

Why This is Problematic: While this approach can work, it's more error-prone and harder to debug. It mixes the collection phase with the grouping phase, making the logic less clear.

The Solution: Keep the two phases separate - first collect all people by their required group size, then partition them into groups. This makes the code cleaner and easier to understand:

# Phase 1: Collect
groups_by_size = defaultdict(list)
for i, v in enumerate(groupSizes):
    groups_by_size[v].append(i)

# Phase 2: Partition
result = []
for group_size, people in groups_by_size.items():
    for j in range(0, len(people), group_size):
        result.append(people[j:j + group_size])

Pitfall 3: Using Wrong Variable in Slicing

The Problem: When using nested list comprehension or loops, it's easy to mix up variables:

# Wrong: using 'v' (the list) instead of 'i' (the group size) for step
return [v[j : j + i] for i, v in g.items() for j in range(0, len(v), v)]
                                                                        # ^ Wrong!

Why This Happens: With variables like i, v, and j, it's easy to confuse which represents what, especially in compact list comprehensions.

The Solution: Use descriptive variable names to avoid confusion:

result = []
for group_size, people_list in groups_by_size.items():
    for start_idx in range(0, len(people_list), group_size):
        result.append(people_list[start_idx:start_idx + group_size])
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

How does quick sort divide the problem into subproblems?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More