Happy Students
MedPrompt
You are given a 0-indexed integer array nums of length n where n is the total number of students in the class. The class teacher tries to select a group of students so that all the students remain happy.
The ith student will become happy if one of these two conditions is met:
- The student is selected and the total number of selected students is strictly greater than
nums[i]. - The student is not selected and the total number of selected students is strictly less than
nums[i].
Return the number of ways to select a group of students so that everyone remains happy.
Example 1:
Input: nums = [1,1]
Output: 2
Explanation:
The two possible ways are:
The class teacher selects no student.
The class teacher selects both students to form the group.
If the class teacher selects just one student to form a group then the both students will not be happy. Therefore, there are only two possible ways.Example 2:
Input: nums = [6,0,3,3,6,7,2,7]
Output: 3
Explanation:
The three possible ways are:
The class teacher selects the student with index = 1 to form the group.
The class teacher selects the students with index = 1, 2, 3, 6 to form the group.
The class teacher selects all the students to form the group.
Constraints:
1 <= nums.length <= 1050 <= nums[i] < nums.length
Approaches
2 approaches with complexity analysis and trade-offs.
This approach exhaustively checks every possible group of students. A group can be represented as a subset of the students. There are 2^n possible subsets, where n is the number of students. For each subset, we determine its size k and then verify if every student (both in the subset and not in the subset) is happy according to the given conditions.
Algorithm
- Initialize a counter
waysto 0. - Generate every possible subset of students. There are
2^nsuch subsets. - For each subset
S:- Let
kbe the size of the subsetS. - Assume the group is valid (
all_happy = true). - Iterate through all
nstudents:- If a student
iis inS(selected), check ifk > nums[i]. If not, the group is invalid (all_happy = false), and we can stop checking this subset. - If a student
iis not inS(not selected), check ifk < nums[i]. If not, the group is invalid (all_happy = false), and we can stop checking this subset.
- If a student
- If
all_happyremains true after checking all students, incrementways.
- Let
- Return
ways.
Walkthrough
The algorithm iterates through all 2^n subsets of the n students. This can be done using recursion or iteration with bit manipulation (where the i-th bit of a number from 0 to 2^n - 1 represents whether student i is selected).
For each subset S:
- Calculate the size of the group,
k = |S|. - Initialize a flag
all_happy = true. - Iterate through all
nstudents fromi = 0ton-1:- If student
iis in the subsetS(selected): check ifk > nums[i]. If not, setall_happy = falseand break. - If student
iis not in the subsetS(not selected): check ifk < nums[i]. If not, setall_happy = falseand break.
- If student
- If
all_happyis still true after checking all students, it means this subset forms a valid group, so we increment our total count of ways.
After checking all 2^n subsets, the final count is the answer.
Complexity
Time
O(n * 2^n). There are `2^n` subsets to check. For each subset, we iterate through all `n` students to verify the happiness conditions.
Space
O(n) to store the current subset being processed.
Trade-offs
Pros
Conceptually simple and directly follows the problem definition.
Cons
Extremely inefficient due to its exponential time complexity.
Not feasible for the given constraints where
ncan be up to10^5.
A much more efficient approach is based on the observation that if a valid group of size k exists, then the specific group formed by selecting the k students with the smallest nums values must also be valid. This crucial insight allows us to avoid checking every possible combination of students. By sorting the nums array first, we can check just one canonical candidate group for each possible size k (from 0 to n), leading to a much faster algorithm.
Algorithm
- Sort the input list
numsin non-decreasing order. - Initialize a counter
ways = 0. - Check for group size
k=0: If no students are selected, all must be happy. This requires0 < nums[i]for alli. After sorting, this simplifies to0 < nums.get(0). If true, incrementways. - Check for group sizes
1 <= k < n: For eachk, we consider the group formed by the firstkstudents in the sorted list. This group is valid if:- All selected students are happy:
k > nums.get(k-1). - All non-selected students are happy:
k < nums.get(k). - If both conditions are met, increment
ways.
- All selected students are happy:
- Check for group size
k=n: If all students are selected, all must be happy. This requiresn > nums[i]for alli. After sorting, this simplifies ton > nums.get(n-1). If true, incrementways. - Return the total
ways.
Walkthrough
First, we sort the nums list in non-decreasing order. This allows us to easily identify the students with the smallest nums values.
We then iterate through all possible group sizes k from 0 to n and check if a valid group of that size can be formed by selecting the k students with the smallest nums values.
-
Case
k = 0(no students selected): Allnstudents are not selected. For every studentito be happy, the condition is0 < nums[i]. After sorting, we only need to check the smallest value:0 < nums.get(0). If this holds, we count this as one valid way. -
Case
1 <= k < n(kstudents selected): We form a group with the firstkstudents from the sorted list (indices0tok-1).- For the
kselected students to be happy, we needk > nums[i]foriin[0, k-1]. This is equivalent tok > nums.get(k-1). - For the
n-knon-selected students to be happy, we needk < nums[j]forjin[k, n-1]. This is equivalent tok < nums.get(k). - If both
nums.get(k-1) < kandk < nums.get(k)are true, we count thiskas a valid way.
- For the
-
Case
k = n(all students selected): Allnstudents are selected. For every studentito be happy, the condition isn > nums[i]. After sorting, we only need to check the largest value:n > nums.get(n-1). If this holds, we count this as one valid way.
The final answer is the total count accumulated from these cases.
import java.util.Collections;import java.util.List; class Solution { public int happyStudents(List<Integer> nums) { int n = nums.size(); Collections.sort(nums); int count = 0; // Case k = 0: select 0 students // Condition: 0 < nums.get(i) for all i. // After sorting, this is equivalent to 0 < nums.get(0). if (nums.get(0) > 0) { count++; } // Case 1 <= k <= n-1: select k students // Select students at indices 0..k-1. // Condition for selected: k > nums.get(k-1). // Condition for not selected: k < nums.get(k). for (int k = 1; k < n; k++) { if (nums.get(k-1) < k && k < nums.get(k)) { count++; } } // Case k = n: select all n students // Condition: n > nums.get(i) for all i. // After sorting, this is equivalent to n > nums.get(n-1). if (nums.get(n-1) < n) { count++; } return count; }}Complexity
Time
O(n log n). Sorting the list takes `O(n log n)`. The subsequent loop to check all `n+1` possible group sizes takes `O(n)`. The total time is dominated by the sorting step.
Space
O(log n) or O(n), depending on the implementation of the sorting algorithm. Java's `Collections.sort` on a `List` can take up to O(n) space.
Trade-offs
Pros
Highly efficient with a polynomial time complexity.
Correctly solves the problem within the given constraints.
The logic is systematic and covers all possible valid group sizes.
Cons
The logic is more involved than the brute-force approach.
The time complexity is limited by sorting, which might be suboptimal if a faster-than-sorting method existed (though unlikely for this problem).
Solutions
Solution
class Solution {public int countWays(List<Integer> nums) { Collections.sort(nums); int n = nums.size(); int ans = 0; for (int i = 0; i <= n; i++) { if ((i == 0 || nums.get(i - 1) < i) && (i == n || nums.get(i) > i)) { ans++; } } return ans; }}class Solution {public: int countWays(vector<int> &nums) { sort(nums.begin(), nums.end()); int ans = 0; int n = nums.size(); for (int i = 0; i <= n; ++i) { if ((i && nums[i - 1] >= i) || (i < n && nums[i] <= i)) { continue; } ++ans; } return ans; }};class Solution: def countWays(self, nums: List[int]) -> int: nums . sort() n = len(nums) ans = 0 for i in range(n + 1): if i and nums[i - 1] >= i: continue if i < n and nums[i] <= i: continue return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.