Facebook Pixel

3678. Smallest Absent Positive Greater Than Average

EasyArrayHash Table
LeetCode ↗

Problem Description

You are given an integer array nums.

Your task is to find the smallest positive integer that satisfies both of the following conditions:

  1. It is absent from nums — meaning it does not appear anywhere in the array.
  2. It is strictly greater than the average of all elements in nums.

The average of the array is defined as the sum of all its elements divided by the number of elements, i.e., sum(nums) / len(nums). Note that the average can be a fractional value, and it can also be negative or zero if the array contains negative numbers.

Return that smallest absent positive integer.

Example

Suppose nums = [3, 5]:

  • The average is (3 + 5) / 2 = 4.
  • We need a positive integer strictly greater than 4 that is not in the array.
  • 5 is greater than 4, but it appears in nums, so it is skipped.
  • 6 is greater than 4 and absent from nums, so the answer is 6.

Suppose nums = [-1, -2, -3]:

  • The average is -2.
  • Any positive integer is already strictly greater than -2.
  • The smallest positive integer is 1, and since 1 does not appear in nums, the answer is 1.

Key Points

  • The answer must always be a positive integer (at least 1), even if the average is negative.
  • The answer must be strictly greater than the average — being equal to it is not enough.
  • The answer must not exist in the array.
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

How We Pick the Algorithm

Why Hash Table / Counting?

This problem maps to Hash Table / Counting through a short path in the full flowchart.

Linkedlist?noFastlookup,counting,yesHash Table /Counting

Store all elements in a hash set for O(1) membership checks, then scan upward from floor(average) + 1 until finding a value not present in the set.

Open in Flowchart

Intuition

The problem gives us two constraints on the answer: it must be strictly greater than the average, and it must be absent from the array. The natural idea is to first figure out the smallest possible candidate, then move upward until we find a valid one.

Let's think about where to start searching:

  • The answer must be a positive integer, so it is at least 1.
  • The answer must be strictly greater than the average avg. The smallest integer strictly greater than avg is ⌊avg⌋ + 1. This works for both fractional averages (e.g., avg = 3.5 gives 4) and exact integer averages (e.g., avg = 4 gives 5, since equality is not allowed).

Combining both conditions, the smallest candidate worth checking is max(1, ⌊avg⌋ + 1). Anything smaller is guaranteed to violate at least one of the two conditions, so there is no reason to look below this value.

Now we only need to handle the "absent" condition. Starting from this candidate, we check whether it exists in the array. If it does, we try the next integer, and so on. Since checking membership in a list is O(n) per query, we put all elements into a hash set first, which makes each lookup O(1).

One more observation guarantees this loop terminates quickly: the array has only n elements, so at most n consecutive candidates can be blocked. Within at most n + 1 steps starting from max(1, ⌊avg⌋ + 1), we are certain to hit a number that is absent from the array. This bounds the total work at O(n) and confirms that a simple linear scan upward is efficient enough — no sorting or complex search is needed.

Solution Approach

We implement the idea in three clear steps, using a hash set as the core data structure.

Step 1: Build a hash set of the elements

s = set(nums)

We store all elements of nums in a set s. This allows us to check whether a candidate number exists in the array in O(1) average time, instead of scanning the whole list for every check.

Step 2: Compute the starting candidate

ans = max(1, sum(nums) // len(nums) + 1)

This single line combines both constraints into the smallest possible starting point:

  • sum(nums) // len(nums) is the floor of the average, ⌊avg⌋. In Python, the // operator performs floor division, which rounds toward negative infinity, so this works correctly even when the sum is negative (e.g., -5 // 2 = -3).
  • Adding 1 gives ⌊avg⌋ + 1, the smallest integer strictly greater than the average. This holds in both cases:
    • If avg is fractional (say 3.5), then ⌊avg⌋ + 1 = 4 > 3.5. ✓
    • If avg is an exact integer (say 4), then ⌊avg⌋ + 1 = 5 > 4, correctly skipping 4 itself since equality is forbidden. ✓
  • Wrapping it in max(1, ...) enforces the positive requirement: if the average is negative or zero, the candidate could drop below 1, so we clamp it up to 1.

Step 3: Scan upward until the candidate is absent

while ans in s:
    ans += 1
return ans

Starting from the candidate, we repeatedly check membership in the set. If the current value exists in nums, we move to the next integer. The loop is guaranteed to stop within at most n + 1 iterations, because the set contains only n elements and cannot block more than n consecutive integers.

Complete Code

class Solution:
    def smallestAbsent(self, nums: List[int]) -> int:
        s = set(nums)
        ans = max(1, sum(nums) // len(nums) + 1)
        while ans in s:
            ans += 1
        return ans

Complexity Analysis

Let n be the length of nums.

  • Time complexity: O(n) — building the set takes O(n), computing the sum takes O(n), and the while loop runs at most n + 1 times with O(1) lookups each.
  • Space complexity: O(n) — the hash set stores up to n distinct elements.

Example Walkthrough

Let's trace through the solution with nums = [4, 5, 2, 6].

Step 1: Build the hash set

s = set(nums)  # s = {2, 4, 5, 6}

All four elements go into the set, so each future membership check costs O(1).

Step 2: Compute the starting candidate

  • Sum: 4 + 5 + 2 + 6 = 17
  • Length: 4
  • Floor of the average: 17 // 4 = 4 (the true average is 4.25)
  • Add one: 4 + 1 = 5 — the smallest integer strictly greater than 4.25
  • Clamp to positive: max(1, 5) = 5

So ans = 5. Nothing below 5 can be valid: 4 and below fail the "strictly greater than the average" condition.

Step 3: Scan upward until the candidate is absent

CandidateIn set {2, 4, 5, 6}?Action
5YesIncrement to 6
6YesIncrement to 7
7NoReturn 7

The answer is 7: it is strictly greater than 4.25 and does not appear in the array.

A second case — negative average, nums = [-3, -6]:

  • Sum: -9, floor division: -9 // 2 = -5 (Python floors toward negative infinity), plus one gives -4.
  • The clamp kicks in: max(1, -4) = 1, so ans = 1.
  • 1 is not in {-3, -6}, so the loop exits immediately and returns 1.

This second case highlights why the max(1, ...) clamp matters: without it, the candidate would start at -4, which violates the positivity requirement even though it exceeds the average.

Solution Implementation

1class Solution:
2    def smallestAbsent(self, nums: List[int]) -> int:
3        # Store all numbers in a set for O(1) membership lookups
4        num_set = set(nums)
5      
6        # Compute the floor of the average, then add 1 to get the smallest
7        # integer strictly greater than the average.
8        # Clamp the result to at least 1, since the answer must be positive.
9        candidate = max(1, sum(nums) // len(nums) + 1)
10      
11        # Increment the candidate until it is not present in the array
12        while candidate in num_set:
13            candidate += 1
14      
15        return candidate
16
1class Solution {
2    public int smallestAbsent(int[] nums) {
3        // Store all elements in a hash set for O(1) lookups
4        Set<Integer> numSet = new HashSet<>();
5        int sum = 0;
6
7        // Build the set and accumulate the total sum in a single pass
8        for (int num : nums) {
9            numSet.add(num);
10            sum += num;
11        }
12
13        // The candidate must be a positive integer strictly greater
14        // than the average, so start from max(1, average + 1)
15        int candidate = Math.max(1, sum / nums.length + 1);
16
17        // Increment the candidate until we find a value
18        // that does not appear in the array
19        while (numSet.contains(candidate)) {
20            ++candidate;
21        }
22
23        return candidate;
24    }
25}
26
1class Solution {
2public:
3    int smallestAbsent(vector<int>& nums) {
4        // Store all elements in a hash set for O(1) lookups
5        unordered_set<int> numSet;
6        // Accumulate the sum of all elements
7        int totalSum = 0;
8        for (int num : nums) {
9            numSet.insert(num);
10            totalSum += num;
11        }
12
13        // Compute the average (integer division) of the array.
14        // The answer must be a positive integer strictly greater than the average,
15        // so start searching from max(1, average + 1).
16        int candidate = max(1, totalSum / static_cast<int>(nums.size()) + 1);
17
18        // Increment the candidate until we find a value not present in the array
19        while (numSet.contains(candidate)) {
20            ++candidate;
21        }
22
23        return candidate;
24    }
25};
26
1/**
2 * Finds the smallest positive integer that is strictly greater than
3 * the average of all elements in `nums` and does not appear in `nums`.
4 *
5 * Steps:
6 * 1. Store all numbers in a hash set for O(1) lookups.
7 * 2. Compute the sum of all elements to derive the average.
8 * 3. Start from the first candidate: the larger of 1 and floor(average) + 1.
9 * 4. Increment the candidate until it is absent from the set.
10 *
11 * Time complexity:  O(n + k), where n is the array length and
12 *                   k is the number of candidates checked.
13 * Space complexity: O(n) for the hash set.
14 *
15 * @param nums - The input array of integers.
16 * @returns The smallest absent integer greater than the average.
17 */
18function smallestAbsent(nums: number[]): number {
19    // Hash set for constant-time membership checks
20    const numSet = new Set<number>(nums);
21
22    // Sum of all elements, used to compute the average
23    const totalSum = nums.reduce((acc, cur) => acc + cur, 0);
24
25    // The answer must be a positive integer strictly greater than the average,
26    // so start from max(1, floor(average) + 1)
27    let candidate = Math.max(1, Math.floor(totalSum / nums.length) + 1);
28
29    // Move forward until the candidate is not present in the array
30    while (numSet.has(candidate)) {
31        candidate++;
32    }
33
34    return candidate;
35}
36

Time and Space Complexity

  • Time Complexity: O(n), where n is the length of the array nums. Building the set s and computing sum(nums) each take O(n) time. The while loop increments ans only while it exists in s; since s contains at most n distinct values, the loop executes at most n + 1 iterations, with each membership check ans in s taking O(1) on average. Therefore, the total time is O(n) + O(n) + O(n) = O(n).

  • Space Complexity: O(n). The set s stores up to n distinct elements from nums, which dominates the extra space used. All other variables (ans, intermediate results) require only O(1) additional space.

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

Common Pitfalls

Pitfall 1: Mishandling an average that is an exact integer

A frequent mistake is to compute the starting candidate with math.ceil(sum(nums) / len(nums)), reasoning that "the answer is the ceiling of the average."

# WRONG
candidate = max(1, math.ceil(sum(nums) / len(nums)))

This works when the average is fractional (ceil(3.5) = 4 > 3.5 ✓), but breaks when the average is a whole number. For nums = [3, 5], the average is exactly 4, and ceil(4) = 4, which is equal to the average — not strictly greater. The condition demands strict inequality, so the correct starting point is 5 (and after skipping the present 5, the answer is 6).

Solution: Use floor(avg) + 1 instead, which is strictly greater than the average in both cases:

candidate = max(1, sum(nums) // len(nums) + 1)
  • Fractional average 3.53 + 1 = 4 > 3.5
  • Integer average 44 + 1 = 5 > 4

Pitfall 2: Negative averages and language-specific division semantics

The trick sum // len + 1 relies on floor division, which in Python rounds toward negative infinity (-5 // 2 == -3). When porting this solution to languages like Java, C++, or Go, integer division truncates toward zero (-5 / 2 == -2), producing a value one too large for negative non-exact averages and potentially yielding a candidate that is not strictly greater than... actually worse, a candidate that wastes correctness guarantees of the formula.

// WRONG in Java: -5 / 2 == -2, but floor(-2.5) == -3
int candidate = Math.max(1, sum / n + 1);

In this particular problem the max(1, ...) clamp often masks the bug (negative averages get clamped to 1 anyway), but the floor value itself is wrong, and the same pattern copied into a similar problem without the clamp will fail.

Solution: Compute a true floor explicitly, e.g. in Java:

int floorAvg = Math.floorDiv(sum, n);
int candidate = Math.max(1, floorAvg + 1);

Also note: forgetting the max(1, ...) clamp entirely is its own bug — for nums = [-1, -2, -3] the formula alone gives -2 + 1 = -1, a non-positive starting point, and the loop could return a negative "answer."

Pitfall 3: Floating-point comparison instead of integer arithmetic

Some implementations compute the average as a float and compare candidates against it directly:

# RISKY
avg = sum(nums) / len(nums)
candidate = 1
while candidate in num_set or candidate <= avg:
    candidate += 1

Beyond being slower (it may iterate from 1 all the way up past a large average), this introduces floating-point precision risk: for large sums, sum(nums) / len(nums) may not be exactly representable, and a comparison like candidate <= avg can be decided incorrectly at the boundary, off by one.

Solution: Stay in integer arithmetic. The strict inequality candidate > sum/len is exactly equivalent to the overflow-free, precision-free integer test candidate * len(nums) > sum(nums), or more simply, start directly from sum(nums) // len(nums) + 1 as the reference solution does.

Ready to land your dream job?

Unlock your dream job with a 5-minute quiz for a personalized study roadmap!

Get My Roadmap
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Get a Personalized Study Roadmap:

Which of the following shows the order of node visit in a Breadth-first Search?


Recommended Readings

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

Load More