Facebook Pixel

2733. Neither Minimum nor Maximum

Problem Description

You are given an integer array nums that contains distinct positive integers. Your task is to find and return any number from the array that is neither the minimum value nor the maximum value. If no such number exists, return -1.

The problem is asking you to:

  1. Identify the minimum and maximum values in the array
  2. Find any element that is not equal to either of these extreme values
  3. Return that element, or -1 if all elements are either the minimum or maximum

For example:

  • If nums = [3, 1, 5, 2], the minimum is 1 and maximum is 5. You can return either 3 or 2 since both are neither minimum nor maximum.
  • If nums = [1, 2], the minimum is 1 and maximum is 2. Since there are no other elements, return -1.
  • If nums = [5], there's only one element which is both minimum and maximum, so return -1.

The solution approach finds the minimum and maximum values using min() and max() functions, then uses a generator expression with next() to find the first element that doesn't equal either extreme value. If no such element exists, it returns the default value -1.

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

Intuition

The key insight is that we need to find an element that falls between the minimum and maximum values. Since we're looking for any element that is neither the smallest nor the largest, we can think of this problem as finding a "middle" element.

The straightforward approach is to first identify what we need to avoid - the minimum and maximum values. Once we know these two values, we can scan through the array and pick the first element that doesn't match either of them.

Why does this work? Because if an array has at least 3 distinct elements, there must be at least one element that is neither the minimum nor the maximum. If the array has only 1 or 2 elements, then every element is either a minimum or maximum (or both), so we return -1.

The solution uses next() with a generator expression (x for x in nums if x != mi and x != mx) which efficiently finds the first qualifying element without needing to check all elements. The second parameter -1 in next() serves as a default value when no element satisfies our condition, which happens when the array has fewer than 3 distinct values.

This approach is optimal because:

  1. We must examine all elements at least once to find the minimum and maximum (no way around this)
  2. In the worst case, we might need to check all elements again to find a middle value
  3. The solution combines both steps efficiently in a single pass after finding min/max

Learn more about Sorting patterns.

Solution Approach

The implementation follows a simple two-step process:

Step 1: Find the minimum and maximum values

mi, mx = min(nums), max(nums)

We use Python's built-in min() and max() functions to find the smallest and largest values in the array. These functions iterate through the array once each, giving us O(n) time complexity for this step.

Step 2: Search for a value that's neither minimum nor maximum

return next((x for x in nums if x != mi and x != mx), -1)

This line uses a generator expression combined with the next() function:

  • (x for x in nums if x != mi and x != mx) creates a generator that yields elements from nums that are neither equal to mi nor mx
  • next() retrieves the first value from this generator
  • The second parameter -1 is the default value returned if the generator is empty (no elements satisfy the condition)

Why this works:

  • If the array has 3 or more distinct elements, at least one element must be between the minimum and maximum values
  • If the array has only 1 or 2 distinct elements, all elements are either minimum or maximum, so the generator produces nothing and next() returns -1

Time and Space Complexity:

  • Time Complexity: O(n) where n is the length of the array. We traverse the array to find min/max, and potentially traverse it again to find a middle element.
  • Space Complexity: O(1) as we only use a constant amount of extra space for variables mi and mx.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through the solution with nums = [4, 2, 7, 1, 5]:

Step 1: Find minimum and maximum

  • We scan through the array to find mi = min(nums) = 1
  • We scan through the array to find mx = max(nums) = 7

Step 2: Find an element that's neither min nor max

  • We create a generator that will check each element:
    • Check 4: Is 4 != 1 and 4 != 7? Yes! The generator yields 4
    • next() immediately returns this first yielded value: 4

The function returns 4 (we could have also returned 2 or 5, but 4 is encountered first).

Let's also see an example where we return -1: With nums = [3, 3]:

Step 1: Find minimum and maximum

  • mi = min(nums) = 3
  • mx = max(nums) = 3

Step 2: Find an element that's neither min nor max

  • We check each element:
    • Check first 3: Is 3 != 3 and 3 != 3? No, skip
    • Check second 3: Is 3 != 3 and 3 != 3? No, skip
  • The generator yields nothing
  • next() returns the default value: -1

This demonstrates how the solution efficiently handles both cases - when a middle element exists and when it doesn't.

Solution Implementation

1class Solution:
2    def findNonMinOrMax(self, nums: List[int]) -> int:
3        """
4        Find an element in the array that is neither the minimum nor the maximum.
5      
6        Args:
7            nums: List of integers
8          
9        Returns:
10            An element that is neither min nor max, or -1 if no such element exists
11        """
12        # Find the minimum and maximum values in the array
13        min_value = min(nums)
14        max_value = max(nums)
15      
16        # Iterate through the array to find the first element that is neither min nor max
17        # Using a generator expression with next() for efficient early termination
18        result = next(
19            (num for num in nums if num != min_value and num != max_value), 
20            -1  # Default value if no such element is found
21        )
22      
23        return result
24
1class Solution {
2    /**
3     * Finds any element in the array that is neither the minimum nor the maximum value.
4     * 
5     * @param nums The input array of integers
6     * @return An element that is neither min nor max, or -1 if no such element exists
7     */
8    public int findNonMinOrMax(int[] nums) {
9        // Initialize variables to track minimum and maximum values
10        // minValue starts at upper bound (100 based on problem constraints)
11        // maxValue starts at lower bound (0)
12        int minValue = 100;
13        int maxValue = 0;
14      
15        // First pass: Find the minimum and maximum values in the array
16        for (int currentNum : nums) {
17            minValue = Math.min(minValue, currentNum);
18            maxValue = Math.max(maxValue, currentNum);
19        }
20      
21        // Second pass: Find and return the first element that is neither min nor max
22        for (int currentNum : nums) {
23            if (currentNum != minValue && currentNum != maxValue) {
24                return currentNum;
25            }
26        }
27      
28        // If no such element exists (e.g., all elements are the same or only two distinct values exist)
29        return -1;
30    }
31}
32
1class Solution {
2public:
3    int findNonMinOrMax(vector<int>& nums) {
4        // Find both minimum and maximum elements in the array simultaneously
5        auto [minIter, maxIter] = minmax_element(nums.begin(), nums.end());
6      
7        // Iterate through the array to find an element that is neither min nor max
8        for (int num : nums) {
9            // If current element is not the minimum and not the maximum, return it
10            if (num != *minIter && num != *maxIter) {
11                return num;
12            }
13        }
14      
15        // If no such element exists (all elements are either min or max), return -1
16        return -1;
17    }
18};
19
1function findNonMinOrMax(nums: number[]): number {
2    // Handle edge case: array with less than 3 elements cannot have a non-min/max value
3    if (nums.length < 3) {
4        return -1;
5    }
6  
7    // Find the minimum and maximum values in the array
8    const minValue: number = Math.min(...nums);
9    const maxValue: number = Math.max(...nums);
10  
11    // Iterate through the array to find an element that is neither min nor max
12    for (const num of nums) {
13        // If current element is not the minimum and not the maximum, return it
14        if (num !== minValue && num !== maxValue) {
15            return num;
16        }
17    }
18  
19    // If no such element exists (all elements are either min or max), return -1
20    return -1;
21}
22

Time and Space Complexity

The time complexity is O(n), where n is the length of the array. This is because:

  • min(nums) traverses the entire array once: O(n)
  • max(nums) traverses the entire array once: O(n)
  • The generator expression (x for x in nums if x != mi and x != mx) in the worst case iterates through all elements: O(n)
  • Overall: O(n) + O(n) + O(n) = O(n)

The space complexity is O(1). The algorithm only uses a constant amount of extra space:

  • Two variables mi and mx to store the minimum and maximum values
  • The generator expression creates an iterator that yields values one at a time without storing the entire filtered list
  • No additional data structures that scale with input size are created

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

Common Pitfalls

1. Inefficient Multiple Passes Through the Array

A common pitfall is making unnecessary multiple passes through the array when the problem can be solved more efficiently.

Pitfall Example:

def findNonMinOrMax(self, nums: List[int]) -> int:
    min_val = min(nums)  # First pass
    max_val = max(nums)  # Second pass
  
    # Third pass through entire array even if answer is found early
    for num in nums:
        if num != min_val and num != max_val:
            return num
    return -1

Better Solution: For small arrays or when we expect to find the answer quickly, we can optimize by combining operations:

def findNonMinOrMax(self, nums: List[int]) -> int:
    if len(nums) < 3:
        return -1  # Early termination for impossible cases
  
    # Find min, max, and a middle value in a single pass
    min_val = max_val = nums[0]
    middle = -1
  
    for num in nums:
        if num < min_val:
            middle = min_val  # Previous min becomes a potential middle
            min_val = num
        elif num > max_val:
            middle = max_val  # Previous max becomes a potential middle
            max_val = num
        elif num != min_val and num != max_val:
            return num  # Found answer immediately
  
    return middle if middle != -1 and middle != min_val and middle != max_val else -1

2. Not Handling Edge Cases Efficiently

Pitfall Example: Processing the entire array even when the array size makes it impossible to have a middle element:

def findNonMinOrMax(self, nums: List[int]) -> int:
    min_val = min(nums)  # Unnecessary computation for len <= 2
    max_val = max(nums)  # Unnecessary computation for len <= 2
  
    # Rest of the logic...

Better Solution: Check array length first:

def findNonMinOrMax(self, nums: List[int]) -> int:
    # Quick check for impossible cases
    if len(nums) <= 2:
        return -1
  
    # For arrays with 3+ elements, we can even return early
    # by sorting first 3 elements
    first_three = sorted(nums[:3])
    return first_three[1]  # Middle element is guaranteed to be neither min nor max

3. Using Sorting When Not Necessary

Pitfall Example: Some might think to sort the entire array first:

def findNonMinOrMax(self, nums: List[int]) -> int:
    nums_sorted = sorted(nums)  # O(n log n) - unnecessary overhead
    if len(nums_sorted) < 3:
        return -1
    return nums_sorted[1]  # Return second element

Why it's a pitfall: Sorting takes O(n log n) time when the problem can be solved in O(n) time.

Better Solution: Stick with the linear time approach using min/max functions as shown in the original solution, or use the partial sorting approach shown above for just the first 3 elements when applicable.

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

Which data structure is used to implement priority queue?


Recommended Readings

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

Load More