Skip to main content

Hashing

Group the People Given the Group Size They Belong To Solution In C++/Java/Python/Javascript

Problem Description

In this problem, we have n individuals, each identified by a unique ID ranging from 0 to n-1. Every person belongs to a specific group, but instead of being explicitly assigned to a group, they are only given a number indicating how many people should be in their group. The input array groupSizes provides this information, where groupSizes[i] represents the exact size of the group that person i must be a part of. The goal is to correctly organize all individuals into groups that strictly follow these size constraints, ensuring that each group contains the required number of members.

To achieve this, we need to systematically track and organize people based on their group size requirements. As we process each individual, we group them with others who have the same required group size until a group is complete. Once a group reaches its required size, it is finalized, and we move on to forming the next group. This process continues until every person is assigned to exactly one group. Since multiple valid solutions can exist, any correct arrangement that satisfies the conditions is acceptable. The challenge lies in efficiently forming the groups while ensuring that all constraints are met.
Group the People Given the Group Size They Belong To-problem-Description

Example

Input:
groupSizes = [3,3,3,3,3,1,3]

Output:
[[5],[0,1,2],[3,4,6]]

Explanation:
Each person is assigned a required group size, and we need to form groups accordingly. Person 5 has a group size of 1, meaning they must be alone in a group: [5]. The next three people, 0, 1, and 2, all require a group of size 3, so they are placed together: [0,1,2]. Similarly, people 3, 4, and 6 also require a group of size 3, forming another valid group: [3,4,6]. This grouping satisfies all constraints, ensuring that each person is in exactly one group of the required size.

Input:
groupSizes = [2,1,3,3,3,2]

Output:
[[1],[0,5],[2,3,4]]

Explanation:
Here, person 1 has a group size of 1, meaning they must be placed in a separate group: [1]. Persons 0 and 5 both have a required group size of 2, so they form a group together: [0,5]. Lastly, persons 2, 3, and 4 all need a group of size 3, making them a valid group: [2,3,4]. This grouping ensures that each person is correctly assigned based on their specified group size while maintaining the integrity of the constraints.

Real-World Example

A real-world example of this problem can be seen in organizing teams for a group project. Imagine students in a class specify the exact number of teammates they need. Some students want to work alone, while others require groups of two, three, or more. The teacher’s task is to form these groups so that each student is placed in a team that matches their requirement, ensuring no one is left out or placed in multiple groups.

Constraints

groupSizes.length == n
1 <= n <= 500
1 <= groupSizes[i] <= n

We have a set of people, each with a unique ID and a requirement for the exact number of members in their group. Our task is to organize them into valid groups while ensuring that no one is misplaced or left out. Since multiple valid solutions exist, we need to carefully track and assign individuals to their respective groups.

In this Group the People Given the Group Size They Belong To LeetCode solution, we will be going through the problem step by step, exploring different approaches—from the brute force method to an optimized solution—to ensure efficiency and correctness .

Brute force Approach

Intuition

In the Group the People Given the Group Size They Belong To solution, the goal is to systematically place individuals into groups based on their specified sizes. We go through the list, checking each person's required group size, and gather others who need the same. Once a group reaches its required size, it is finalized, and we move on to forming the next one. This ensures that every individual is placed exactly once, adhering to the given constraints.

Since we repeatedly search for individuals with the same group size, this approach can become inefficient as the input size grows. The process involves multiple passes through the list, leading to unnecessary operations and higher time complexity. While the method guarantees correctness, it does not optimize the grouping process, making it a less scalable solution for larger inputs.

Approach

Step 1: Iterate Through the List
Start by iterating through the groupSizes array to process each individual. For every person at index i, determine the required group size groupSizes[i]. Since multiple groups of the same size may exist, we must track which individuals have already been assigned to a group.

Step 2: Form Groups Manually
For each person, search the remaining list to find other individuals who need the same group size. Collect them into a temporary list until it reaches the required size. Once a group is completed, add it to the final result and mark those individuals as assigned. Continue this process until all people are placed in groups.

Step 3: Repeat the Search for Unassigned Individuals
Since we are manually searching for members each time, iterate over the list multiple times to ensure that all individuals are grouped. This results in repeated lookups, making the approach inefficient for large inputs.

Step 4: Return the Final Grouping
Once all people are assigned to their respective groups, return the final list of groups. While this brute force approach correctly solves the Group the People Given the Group Size They Belong To solution, it is computationally expensive due to redundant searches, leading to a higher time complexity.

Let us understand this with a video,

0:00
/0:42

Group the People Given the Group Size They Belong To-Brute-Force-Approach

Dru Run for Group the People Given the Group Size They Belong To


Input:
groupSizes = [3,3,3,3,3,1,3]

Step 1: Process Each Person

  • Start iterating through the list.
  • First person (0) needs a group of size 3.
  • Search for two more people who also need a group of size 3 → found 1 and 2.
  • Form the group [0,1,2] and mark them as assigned.

Step 2: Continue Assigning Groups

  • Move to the next unassigned person (3), who also needs a group of 3.
  • Find two more people with the same requirement → 4 and 6.
  • Form the group [3,4,6] and mark them as assigned.

Step 3: Assign the Remaining Person

  • Person 5 needs a group of size 1, so they form their own group [5].

Final Output:
[[5], [0,1,2], [3,4,6]]

Code for All Languages
C++ - Group the People Given the Group Size They Belong To - Brute Force
//Group the People Given the Group Size They Belong To solution in cpp
vector<vector<int>> groupThePeople(vector<int>& groupSizes) {
        vector<vector<int>> result;
        vector<bool> visited(groupSizes.size(), false); // To track assigned people

        // Iterate over each person
        for (int i = 0; i < groupSizes.size(); i++) {
            if (!visited[i]) { // If the person is not yet assigned to a group
                int size = groupSizes[i];
                vector<int> group;

                // Find other people who need the same group size
                for (int j = 0; j < groupSizes.size(); j++) {
                    if (!visited[j] && groupSizes[j] == size) {
                        group.push_back(j);
                        visited[j] = true; // Mark person as assigned
                    }
                    // If we reach the required group size, finalize it
                    if (group.size() == size) {
                        result.push_back(group);
                        break;
                    }
                }
            }
        }
        return result;
    }

Java - Group the People Given the Group Size They Belong To - Brute Force
//Group the People Given the Group Size They Belong To solution in Java
 public List<List<Integer>> groupThePeople(int[] groupSizes) {
        List<List<Integer>> result = new ArrayList<>();
        boolean[] visited = new boolean[groupSizes.length]; 
        // Iterate over each person
        for (int i = 0; i < groupSizes.length; i++) {
            if (!visited[i]) { // If the person is not yet assigned to a group
                int size = groupSizes[i];
                List<Integer> group = new ArrayList<>();

                // Find other people who need the same group size
                for (int j = 0; j < groupSizes.length; j++) {
                    if (!visited[j] && groupSizes[j] == size) {
                        group.add(j);
                        visited[j] = true; // Mark person as assigned
                    }
                    // If we reach the required group size, finalize it
                    if (group.size() == size) {
                        result.add(group);
                        break;
                    }
                }
            }
        }
        return result;
    }

Python
#Group the People Given the Group Size They Belong To solution in Python
def groupThePeople(self, groupSizes):
        result = []
        visited = [False] * len(groupSizes)  # To track assigned people

        # Iterate over each person
        for i in range(len(groupSizes)):
            if not visited[i]:  # If the person is not yet assigned to a group
                size = groupSizes[i]
                group = []

                # Find other people who need the same group size
                for j in range(len(groupSizes)):
                    if not visited[j] and groupSizes[j] == size:
                        group.append(j)
                        visited[j] = True  # Mark person as assigned

                    # If we reach the required group size, finalize it
                    if len(group) == size:
                        result.append(group)
                        break

        return result

JavaScript - Group the People Given the Group Size They Belong To - Brute Force
groupThePeople(groupSizes) {
        let result = [];
        let visited = new Array(groupSizes.length).fill(false); // To track assigned people

        // Iterate over each person
        for (let i = 0; i < groupSizes.length; i++) {
            if (!visited[i]) { // If the person is not yet assigned to a group
                let size = groupSizes[i];
                let group = [];

                // Find other people who need the same group size
                for (let j = 0; j < groupSizes.length; j++) {
                    if (!visited[j] && groupSizes[j] === size) {
                        group.push(j);
                        visited[j] = true; // Mark person as assigned
                    }
                    // If we reach the required group size, finalize it
                    if (group.length === size) {
                        result.push(group);
                        break;
                    }
                }
            }
        }
        return result;
    }

Complexity Analysis of the problem

Time Complexity - O(n²) for Group the People Given the Group Size They Belong To

The brute force approach for solving the Group the People Given the Group Size They Belong To solution involves iterating through the list and manually forming groups based on the given sizes. For each person, the algorithm checks if they have already been assigned to a group. If not, it searches for others with the same required group size and groups them together. Once a group reaches its required size, it is finalized, and the process continues for the remaining individuals. While this method ensures correctness, it involves repeated searches, making it inefficient for large inputs.

Since the Group the People Given the Group Size They Belong To solution uses a nested loop, the outer loop runs O(n) times to process each person, and the inner loop performs a search that can take up to O(n) operations in the worst case. This results in an overall time complexity of O(n²). As n grows, the runtime increases significantly, making it inefficient for large values like n = 500. The excessive scanning of the list multiple times contributes to inefficiency, highlighting the need for an optimized approach to reduce redundant operations.

Space Complexity - O(n) for Group the People Given the Group Size They Belong To

The space complexity of the Group the People Given the Group Size They Belong To solution depends on the additional data structures used to store the grouped individuals. In the brute force approach, we utilize a result list to store the final groups and a visited array to keep track of assigned individuals. The visited array takes O(n) space since each person needs a boolean flag to indicate whether they have been placed in a group. Additionally, the result list stores all groups, which in the worst case, can contain all individuals separately, leading to another O(n) space usage.

Thus, the overall space complexity of the Group the People Given the Group Size They Belong To solution is O(n). This is because all individuals must be accounted for in the final grouping, and no extra nested data structures contribute to an additional exponential space requirement. While the brute force approach is inefficient in terms of time complexity, its space complexity remains optimal, as it only stores the necessary information required to form and return the valid groups.

The Group the People Given the Group Size They Belong To problem initially follows a brute force approach where we manually search for individuals with the same group size, leading to repeated lookups and inefficiencies. To optimize this, instead of iterating multiple times, we can use a hash table to efficiently store and track individuals based on their required group size. As we process each person, we dynamically place them in their respective groups within the map. Once a group reaches its required size, it is immediately finalized and stored in the result, eliminating unnecessary searches. This structured approach significantly reduces redundant operations, making the solution more efficient and scalable.

Optimized Approach

Intuition

In the Group the People Given the Group Size They Belong To problem, the key challenge is efficiently organizing people into groups while ensuring that each group meets the required size constraints. Instead of manually searching for ungrouped individuals multiple times, we can take advantage of a hash table to keep track of people based on their group sizes.

The idea is to process each person one by one, placing them into a temporary list corresponding to their required group size. Once a list reaches its full capacity, it is immediately added to the final result, and a new list is created for the next set of people with the same size requirement. This approach allows us to systematically build and finalize groups without unnecessarily repeated searches, leading to a more structured and efficient solution.

Approach

Step 1: Use a Hash Table to Track Groups
Create a hash table where the keys represent group sizes, and the values are lists containing people who need that specific group size. This allows us to efficiently collect individuals in the correct groups without repeated searches.

Step 2: Iterate Through the People and Populate Groups
Loop through the groupSizes list, and for each person, add them to the corresponding list in the hash table based on their required group size. This ensures that all people with the same group size are stored together dynamically.

Step 3: Finalize Groups When They Reach Their Required Size
As soon as a list in the hash table reaches the required size, it is finalized and added to the result. This ensures that each group is stored correctly without any extra processing, and a new list is initialized for the next set of people with the same size.

Step 4: Return the Final Grouping
Once all individuals have been assigned to their respective groups and stored in the result list, return the final grouping. This Group the People Given the Group Size They Belong To solution efficiently organizes people into valid groups in a structured manner, eliminating unnecessary repeated searches.

Let us understand this with a video,

0:00
/1:15

Group the People Given the Group Size They Belong To-Optimal-Approach

Dru Run for Group the People Given the Group Size They Belong To

Input:
groupSizes = {3, 3, 3, 2, 2, 1}

Step 1: Initialize Hash Table
hashTable = {}
result = []

Step 2: Iterate and Populate Groups

Person 0 (size 3):

  • Add to hash table for size 3 group
    hashTable = {3: [0]}

Person 1 (size 3):

  • Add to hash table for size 3 group
    hashTable = {3: [0, 1]}

Person 2 (size 3):

  • Add to hash table for size 3 group
    hashTable = {3: [0, 1, 2]}
  • GROUP COMPLETE for size 3!
  • Add [0, 1, 2] to result
  • Reset hash table for size 3
    result = [[0, 1, 2]]
    hashTable = {}

Person 3 (size 2):

  • Add to hash table for size 2 group
    hashTable = {2: [3]}

Person 4 (size 2):

  • Add to hash table for size 2 group
    hashTable = {2: [3, 4]}
  • GROUP COMPLETE for size 2!
  • Add [3, 4] to result
    result = [[0, 1, 2], [3, 4]]
    hashTable = {}

Person 5 (size 1):

  • Add to hash table for size 1 group
    hashTable = {1: [5]}
  • GROUP COMPLETE for size 1!
  • Add [5] to result
    result = [[0, 1, 2], [3, 4], [5]]
    hashTable = {}

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

Code for All Languages
C++
//Group the People Given the Group Size They Belong To -  Optimized Approach - C++ 
class Solution {
public:
    vector<vector<int>> groupThePeople(vector<int>& groupSizes) {
    unordered_map<int, vector<int>> groups; // Hash table to store groups based on sizes
    vector<vector<int>> result;

    for (int i = 0; i < groupSizes.size(); i++) {
        groups[groupSizes[i]].push_back(i); // Add person to their respective group
        
        // If the group reaches the required size, finalize it
        if (groups[groupSizes[i]].size() == groupSizes[i]) {
            result.push_back(groups[groupSizes[i]]);
            groups[groupSizes[i]].clear(); // Reset the group to start fresh
        }
    }
    return result;
}
};

Java
//Group the People Given the Group Size They Belong To -  Optimized Approach - Java
 public static List<List<Integer>> groupThePeople(int[] groupSizes) {
        Map<Integer, List<Integer>> groups = new HashMap<>(); // Hash table to store groups based on sizes
        List<List<Integer>> result = new ArrayList<>();

        for (int i = 0; i < groupSizes.length; i++) {
            groups.putIfAbsent(groupSizes[i], new ArrayList<>()); // Initialize if key doesn't exist
            groups.get(groupSizes[i]).add(i); // Add person to their respective group

            // If the group reaches the required size, finalize it
            if (groups.get(groupSizes[i]).size() == groupSizes[i]) {
                result.add(new ArrayList<>(groups.get(groupSizes[i]))); // Add to result
                groups.get(groupSizes[i]).clear(); // Reset the group to start fresh
            }
        }
        return result;
    }

Python
# Group the People Given the Group Size They Belong To -  Optimized Approach - Python
def groupThePeople(groupSizes):
    groups = {}  # Dictionary to store groups based on sizes
    result = []

    for i in range(len(groupSizes)):
        if groupSizes[i] not in groups:
            groups[groupSizes[i]] = []  # Initialize the group if not present
        
        groups[groupSizes[i]].append(i)  # Add person to their respective group

        # If the group reaches the required size, finalize it
        if len(groups[groupSizes[i]]) == groupSizes[i]:
            result.append(groups[groupSizes[i]])  # Add to result
            groups[groupSizes[i]] = []  # Reset the group to start fresh

    return result

Javascript
function groupThePeople(groupSizes) {
    let groups = new Map(); // Hash table to store groups based on sizes
    let result = [];

    for (let i = 0; i < groupSizes.length; i++) {
        if (!groups.has(groupSizes[i])) {
            groups.set(groupSizes[i], []); // Initialize if key doesn't exist
        }
        
        groups.get(groupSizes[i]).push(i); // Add person to their respective group

        // If the group reaches the required size, finalize it
        if (groups.get(groupSizes[i]).length === groupSizes[i]) {
            result.push([...groups.get(groupSizes[i])]); // Add to result
            groups.set(groupSizes[i], []); // Reset the group to start fresh
        }
    }
    return result;
}

Time Complexity - Group the People Given the Group Size They Belong To - O(n)

The optimized approach efficiently groups individuals using a hash table, where the keys represent group sizes and the values store lists of people with that requirement. As we iterate through the groupSizes array, each person is placed into their respective group in O(1) time on average. When a group reaches its required size, it is added to the final result, and the list is cleared for future assignments. This ensures that every individual is processed only once without redundant operations.

Since each element is inserted and retrieved from the hash table in constant time, the overall time complexity remains O(n), where n is the number of people. The use of a hash map allows for efficient tracking and finalization of groups without extra iterations. This makes the solution highly scalable and optimal, even for large input sizes.

Space Complexity - Group the People Given the Group Size They Belong To - O(n)

The optimized approach utilizes extra space for storing group assignments in a hash table, where keys represent group sizes and values store lists of individuals belonging to those groups. Since each person is stored in the hash table before being moved to the final result, the worst-case space requirement for the hash table is O(n). Additionally, the final result also stores all individuals in groups, contributing another O(n) space.

Since no additional nested structures are used beyond the hash table and result list, the overall space complexity remains O(n). This is the minimum space required to store and return the correct grouping of individuals, making the approach memory-efficient while ensuring correctness.