2996. Smallest Missing Integer Greater Than Sequential Prefix Sum


Problem Description

You are given an array of integers called nums, which starts at index 0. We consider a "sequential prefix" of this array to be a section of the array starting at index 0 and continuing up to some index i. This sequential prefix is only considered sequential if every element after the first (from nums[1] to nums[i]) is exactly 1 greater than the element that comes before it. Even just the first element nums[0] by itself counts as a sequential prefix.

The task is to find the smallest integer x that is not present in the array nums, with the condition that x must be equal to or greater than the sum of the elements in the longest sequential prefix of the array. In essence, we're looking for the smallest missing number that's also higher than or equal to this particular sum.

Intuition

The intuition behind the solution begins with identifying the sum of the longest sequential prefix of the array. Since the array is 0-indexed, we start with the first element (the sum s starts as nums[0]) and check the subsequent elements to see if they continue the sequence by incrementing by 1.

Once we reach an element that breaks this rule (not one greater than its predecessor), or we reach the end of the array, we stop. At this point, we have the sum s of the longest sequential prefix.

The next step is to find the smallest integer x not in the array but also greater than or equal to our sum s. To do this efficiently, we can use a hash table (or set in Python) that allows us to quickly check if an integer is part of the nums array or not. We start iterating from our calculated sum s and check each consecutive integer. The moment we hit an integer not in our hash table, we have found our x, and that integer is our answer.

By utilizing the hash table, we avoid iterating over the nums array repeatedly, which would increase the time complexity significantly. Instead, we get a fast lookup and can thus solve the problem more efficiently.

Learn more about Sorting patterns.

Not Sure What to Study? Take the 2-min Quiz to Find Your Missing Piece:

Which data structure is used to implement priority queue?

Solution Approach

The solution approach to the given problem could be broken down into the following algorithmic steps:

  1. Initialization: We start by initializing the sum s to the first element of the array nums[0]. We also set a pointer j to 1 as we will start checking for the continuity of the sequential prefix from the second element.

  2. Finding the Longest Sequential Prefix: We enter a while loop that continues as long as j is less than the length of nums and the current element nums[j] is exactly 1 more than the previous element nums[j - 1]. Inside this loop, we keep adding nums[j] to our sum s and increment j after each iteration.

  3. Hash Table Creation: After finding the sum of the longest sequential prefix, we prepare for the missing integer search by creating a set (acting as a hash table) called vis which includes all the elements of nums. This is done so that we can later check in constant time whether an element is present in nums or not.

  4. Finding the Missing Integer: With the hash table ready, we iterate through integers starting from s using a counter. For each value x, we check if x is not present in the set vis. The first integer x which is not found in the set is the smallest missing integer that is also greater than or equal to the sum s of the longest sequential prefix. As soon as we find such an x, it is returned as the result.

The algorithm exploits the properties of a hash table (in Python, a set) to ensure that the check if an integer is a part of the nums array happens in constant time. This is a critical step in ensuring that the algorithm is efficient even when dealing with large arrays.

Moreover, the use of a counter iteration starting from s is a simple and effective way to search for the missing integer, since it guarantees that we will eventually find the smallest integer not in the array and also meet the condition of being greater than or equal to s.

Here's a reference to the actual implementation from the solution code and the approach:

1class Solution:
2    def missingInteger(self, nums: List[int]) -> int:
3        s, j = nums[0], 1
4        while j < len(nums) and nums[j] == nums[j - 1] + 1:
5            s += nums[j]
6            j += 1
7        vis = set(nums)
8        for x in count(s):
9            if x not in vis:
10                return x

In the code snippet, the count function effectively iterates from s indefinitely until the condition for the missing integer is met.

Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:

Given a sorted array of integers and an integer called target, find the element that equals to the target and return its index. Select the correct code that fills the ___ in the given code snippet.

1def binary_search(arr, target):
2    left, right = 0, len(arr) - 1
3    while left ___ right:
4        mid = (left + right) // 2
5        if arr[mid] == target:
6            return mid
7        if arr[mid] < target:
8            ___ = mid + 1
9        else:
10            ___ = mid - 1
11    return -1
12
1public static int binarySearch(int[] arr, int target) {
2    int left = 0;
3    int right = arr.length - 1;
4
5    while (left ___ right) {
6        int mid = left + (right - left) / 2;
7        if (arr[mid] == target) return mid;
8        if (arr[mid] < target) {
9            ___ = mid + 1;
10        } else {
11            ___ = mid - 1;
12        }
13    }
14    return -1;
15}
16
1function binarySearch(arr, target) {
2    let left = 0;
3    let right = arr.length - 1;
4
5    while (left ___ right) {
6        let mid = left + Math.trunc((right - left) / 2);
7        if (arr[mid] == target) return mid;
8        if (arr[mid] < target) {
9            ___ = mid + 1;
10        } else {
11            ___ = mid - 1;
12        }
13    }
14    return -1;
15}
16

Example Walkthrough

Let's illustrate the solution approach with a simple example. Suppose we have the following array of integers:

nums = [1, 2, 3, 5, 6]

First, we initialize s = nums[0], which is 1 in this case, and set a pointer j to 1 to start checking the continuity from the second element.

Now, we iterate through the array to find the longest sequential prefix:

  • nums[1] is 2, which is nums[0] + 1. So, we add 2 to s, making s = 3, and increment j to 2.
  • nums[2] is 3, which is nums[1] + 1. We add 3 to s, now s = 6, and increment j to 3.
  • nums[3] is 5, which is not nums[2] + 1. The sequence is broken here, so we stop the iteration.

The sum s of the longest sequential prefix is 6.

Next, we create a set vis that contains all elements from nums to facilitate constant time checks for the presence of an element in the array.

We then start iterating from s (which is 6) to find the smallest missing integer x that is also greater than or equal to s and not in nums.

  • We check 6 first, but 6 is in vis. So, we move to the next integer, 7.
  • 7 is not present in vis, which means it is the smallest missing integer fulfilling the conditions.

Therefore, the function would return 7 as the answer for this input array.

Solution Implementation

1from typing import List
2from itertools import count
3
4class Solution:
5    def missingInteger(self, nums: List[int]) -> int:
6        # Initialize sum with the first element and start index at 1
7        cumulative_sum, index = nums[0], 1
8
9        # Loop to calculate the sum of consecutive integers
10        while index < len(nums) and nums[index] == nums[index - 1] + 1:
11            cumulative_sum += nums[index]
12            index += 1
13      
14        # Create a set for nums, it would help in fast look-up
15        visited_numbers = set(nums)
16      
17        # This starts a count from the sum of consecutive numbers
18        # until it finds an integer not present in nums
19        for missing_number in count(cumulative_sum):
20            if missing_number not in visited_numbers:
21                return missing_number
22
1class Solution {
2    public int missingInteger(int[] nums) {
3        // Initialize sum with the first element of the array
4        int sum = nums[0];
5      
6        // Calculate the sum of consecutive numbers starting with nums[0]
7        // Stop if the current number isn't consecutive to the previous one
8        for (int i = 1; i < nums.length && nums[i] == nums[i - 1] + 1; ++i) {
9            sum += nums[i];
10        }
11      
12        // Create a boolean array to track visited numbers up to 50
13        boolean[] visited = new boolean[51];
14      
15        // Fill the visited array, marking each number in 'nums' as visited
16        for (int num : nums) {
17            visited[num] = true;
18        }
19      
20        // Start checking for the missing integer starting from the calculated sum
21        for (int i = sum;; ++i) {
22            // Return the first unvisited number or the number beyond the range of visited
23            if (i >= visited.length || !visited[i]) {
24                return i;
25            }
26        }
27    }
28}
29
1#include <vector>
2#include <bitset>
3
4class Solution {
5public:
6    int missingInteger(vector<int>& nums) {
7        // Initialize sum with the first integer in the array.
8        int sum = nums[0];
9
10        // This loop adds up a sequence of consecutive integers starting from the first element,
11        // stopping when a non-consecutive number is found.
12        for (int i = 1; i < nums.size() && nums[i] == nums[i - 1] + 1; ++i) {
13            sum += nums[i];
14        }
15
16        // Initialize a bitset where its size is one more than the maximum value we expect to check.
17        // This is a compact way to store whether each number is present or not.
18        bitset<51> visited;
19
20        // Fill the bitset by setting the bit corresponding to each number in the 'nums' vector to 1 (true).
21        for (int num : nums) {
22            visited[num] = 1;
23        }
24
25        // Starting from the sum (the smallest possible missing number in a sequence),
26        // incrementally check if each number is missing by checking the bitset.
27        // Note: The condition 'x < 51' is technically not needed if 'nums' only contains numbers in valid range.
28        for (int x = sum; x < 51; ++x) {
29            // If a number is not visited (i.e., the corresponding bit is 0), it's missing from the sequence.
30            if (!visited[x]) {
31                // Return the first missing number encountered.
32                return x;
33            }
34        }
35
36        // In case all numbers up to 50 are present, just return 51.
37        return 51;
38    }
39};
40
1function missingInteger(nums: number[]): number {
2    // Start by initializing the sum 's' with the first number in the array
3    let sum = nums[0];
4
5    // Iterate through the array, checking if the current number is consecutive
6    for (let j = 1; j < nums.length && nums[j] === nums[j - 1] + 1; ++j) {
7        // Add the current number to the sum if it's the consecutive number
8        sum += nums[j];
9    }
10
11    // Create a Set to efficiently check the existence of numbers in 'nums'
12    const visitedNumbers: Set<number> = new Set(nums);
13
14    // Start looking for the missing integer, incrementally checking each number greater than the sum
15    for (let x = sum; ; ++x) { // Infinite loop, will break internally when the missing integer is found
16        if (!visitedNumbers.has(x)) { // Check if the number 'x' is not in the 'visitedNumbers'
17            // Found the missing integer, breaking the loop and returning 'x'
18            return x;
19        }
20    }
21}
22
Not Sure What to Study? Take the 2-min Quiz:

What is the running time of the following code?

1int sqrt(int n) {
2  for (int guess = 1; guess * guess <= n; guess++) {
3    if (guess * guess == n) {
4      return guess;
5    }
6  }
7  return -1;
8}

Time and Space Complexity

Time Complexity

The given Python code consists primarily of a while-loop and a for-loop with an embedded set membership check.

  • The while-loop runs while j is less than the length of the nums array and each consecutive element in nums is one more than the previous element. In the worst case, this loop runs in O(n) time, where n is the length of the nums array, because it can iterate through the entire array in sequence.

  • After the while-loop, the set() operation that creates vis is O(n), because it must visit each element of the nums list once.

  • The for-loop that begins with for x in count(s): is a bit tricky because it relies on what is missing from the nums array. However, it will execute at most n times before finding a missing integer that is not in vis, leading to a O(n) time complexity for this segment as well. The count function from the itertools library generates an iterator starting from the sum s. Each iteration checks whether x is not in vis, which is a O(1) operation because vis is a set.

Therefore, the total time complexity of the code is O(n), because all steps are linear in the size of the input array nums.

Space Complexity

The space complexity of the code comes from the additional data structures used:

  • The vis variable is a set that can contain at most n unique integers from the nums array, giving it O(n) space complexity.

  • There are a fixed number of scalar variables (s and j), which use a constant amount of space, O(1).

  • The count function generates an iterator which doesn't consume additional space proportional to n, since it is just a number generator.

Thus, the overall space complexity of the code is O(n), dominated by the size of the vis set.

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

Fast Track Your Learning with Our Quick Skills Quiz:

In a binary min heap, the minimum element can be found in:


Recommended Readings


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫