title: Average Salary Excluding the Minimum and Maximum Salary
slug: average-salary-excluding-minimum-and-maximum
difficulty: easy
leetcode_id: 1491
leetcode_url: https://leetcode.com/problems/average-salary-excluding-the-minimum-and-maximum-salary/
categories:
  - arrays
  - sorting
patterns:
  - greedy

function_signature: "def average(salary: list[int]) -> float:"

test_cases:
  visible:
    - input: { salary: [4000, 3000, 1000, 2000] }
      expected: 2500.0
    - input: { salary: [1000, 2000, 3000] }
      expected: 2000.0
  hidden:
    - input: { salary: [6000, 5000, 4000, 3000, 2000, 1000] }
      expected: 3500.0
    - input: { salary: [8000, 9000, 2000, 3000, 6000, 1000] }
      expected: 4750.0
    - input: { salary: [48000, 59000, 99000, 13000, 78000, 45000, 31000, 17000, 39000, 37000, 93000, 77000, 33000, 28000, 4000, 54000, 67000, 6000, 1000, 11000] }
      expected: 41111.11111111111
    - input: { salary: [1000, 1001, 1002] }
      expected: 1001.0
    - input: { salary: [100000, 200000, 300000] }
      expected: 200000.0

description: |
  You are given an array of **unique** integers `salary` where `salary[i]` is the salary of the i<sup>th</sup> employee.

  Return *the average salary of employees excluding the minimum and maximum salary*. Answers within `10^-5` of the actual answer will be accepted.

constraints: |
  - `3 <= salary.length <= 100`
  - `1000 <= salary[i] <= 10^6`
  - All the integers of `salary` are **unique**

examples:
  - input: "salary = [4000, 3000, 1000, 2000]"
    output: "2500.00000"
    explanation: "Minimum salary and maximum salary are 1000 and 4000 respectively. Average salary excluding minimum and maximum salary is (2000 + 3000) / 2 = 2500."
  - input: "salary = [1000, 2000, 3000]"
    output: "2000.00000"
    explanation: "Minimum salary and maximum salary are 1000 and 3000 respectively. Average salary excluding minimum and maximum salary is (2000) / 1 = 2000."

explanation:
  intuition: |
    Imagine you're calculating the average performance score of a team, but you want to exclude outliers — the best and worst performers — to get a more representative picture of the typical team member.

    The core insight is straightforward: we need to find the **total sum** of all salaries, then **subtract** the minimum and maximum values, and finally **divide** by the count of remaining employees (which is always `n - 2`).

    Think of it like this: instead of sorting the entire array to identify the min and max (which would work but is overkill), we can find these two values in a single pass through the data. As we scan through, we simply track the smallest and largest values we've seen, while also accumulating the total sum.

    This approach works because we only need to *identify* the min and max — we don't need them in any particular position. Finding extremes is a classic single-pass operation.

  approach: |
    We solve this using a **Single Pass Approach**:

    **Step 1: Initialise tracking variables**

    - `min_salary`: Set to infinity so any salary becomes the new minimum
    - `max_salary`: Set to negative infinity so any salary becomes the new maximum
    - `total_sum`: Set to `0` to accumulate the sum of all salaries

    &nbsp;

    **Step 2: Iterate through the salary array**

    - For each salary, add it to `total_sum`
    - If the salary is less than `min_salary`, update `min_salary`
    - If the salary is greater than `max_salary`, update `max_salary`

    &nbsp;

    **Step 3: Calculate and return the average**

    - Subtract `min_salary` and `max_salary` from `total_sum`
    - Divide by `n - 2` (the count of remaining employees)
    - Return the result as a floating-point number

    &nbsp;

    This single-pass approach is optimal because we gather all necessary information (sum, min, max) in one traversal of the array.

  common_pitfalls:
    - title: Sorting When Not Necessary
      description: |
        A common first instinct is to sort the array and then sum elements from index `1` to `n-2`:

        ```python
        salary.sort()
        return sum(salary[1:-1]) / (len(salary) - 2)
        ```

        While this works and is readable, it has **O(n log n) time complexity** due to the sort. For this problem's constraints (`n <= 100`), it's acceptable, but the single-pass O(n) solution is more elegant and demonstrates better algorithmic thinking.
      wrong_approach: "Sorting the entire array to find min/max"
      correct_approach: "Track min/max while iterating once"

    - title: Integer Division
      description: |
        Be careful with division in languages that distinguish between integer and floating-point division.

        In Python 3, `/` always returns a float, but in Python 2 or other languages, you might accidentally get integer division:

        ```python
        # Wrong in some contexts
        (total - min_val - max_val) // (n - 2)  # Integer division!

        # Correct
        (total - min_val - max_val) / (n - 2)   # Float division
        ```
      wrong_approach: "Using integer division //"
      correct_approach: "Using float division /"

    - title: Off-By-One in Count
      description: |
        Remember that after excluding the minimum and maximum, you have `n - 2` employees remaining, not `n - 1`.

        With 4 employees and 2 excluded, you average over 2 people. A common mistake is dividing by `len(salary) - 1` instead of `len(salary) - 2`.

  key_takeaways:
    - "**Single-pass pattern**: When you only need to find extremes (min/max) and aggregates (sum/count), you can often do it in one traversal"
    - "**Avoid unnecessary sorting**: Sorting is O(n log n) — if you only need min/max, tracking them in O(n) is more efficient"
    - "**Foundation for statistical problems**: This technique of excluding outliers extends to more complex statistical calculations like trimmed means"
    - "**Constraint awareness**: With `n >= 3` guaranteed, we know there's always at least one salary remaining after exclusion"

  time_complexity: "O(n). We traverse the salary array exactly once, performing constant-time operations at each step."
  space_complexity: "O(1). We only use three variables (`min_salary`, `max_salary`, `total_sum`) regardless of input size."

solutions:
  - approach_name: Single Pass
    is_optimal: true
    code: |
      def average(salary: list[int]) -> float:
          # Initialise tracking variables
          min_salary = float('inf')
          max_salary = float('-inf')
          total_sum = 0

          # Single pass to find min, max, and sum
          for s in salary:
              total_sum += s
              if s < min_salary:
                  min_salary = s
              if s > max_salary:
                  max_salary = s

          # Calculate average excluding min and max
          # n - 2 employees remain after exclusion
          return (total_sum - min_salary - max_salary) / (len(salary) - 2)
    explanation: |
      **Time Complexity:** O(n) — Single pass through the array.

      **Space Complexity:** O(1) — Only three tracking variables used.

      We traverse once, tracking the minimum, maximum, and total sum simultaneously. This is optimal because we need to examine each element at least once to determine these values.

  - approach_name: Built-in Functions
    is_optimal: true
    code: |
      def average(salary: list[int]) -> float:
          # Use built-in functions for clarity
          # Each of min(), max(), sum() is O(n), but constant factor is low
          return (sum(salary) - min(salary) - max(salary)) / (len(salary) - 2)
    explanation: |
      **Time Complexity:** O(n) — Three passes through the array (sum, min, max).

      **Space Complexity:** O(1) — No additional data structures.

      This one-liner is equally optimal in big-O terms and arguably more readable. While it makes three passes instead of one, the constant factor difference is negligible for this problem's constraints. In interviews, this concise approach demonstrates knowledge of Python's built-in functions.

  - approach_name: Sorting
    is_optimal: false
    code: |
      def average(salary: list[int]) -> float:
          # Sort to put min at start, max at end
          salary.sort()

          # Sum everything except first and last elements
          middle_sum = sum(salary[1:-1])

          # Average over n - 2 employees
          return middle_sum / (len(salary) - 2)
    explanation: |
      **Time Complexity:** O(n log n) — Dominated by the sorting step.

      **Space Complexity:** O(1) or O(n) — Depends on the sorting algorithm used.

      While correct and readable, sorting is overkill for this problem. We only need min and max, not a fully sorted array. However, this approach is easy to understand and may be acceptable in interviews when time is limited. It also naturally handles the case where we need to sum the middle elements.
