Hacker News

A software engineering interview question I like: computing the median

Handling Edge Cases

The first thing to consider is what happens when the input list is empty. The function should raise an exception rather than returning a sentinel value, since None or 0 could be valid median values in other contexts.

def median(numbers: list[float]) -> float:
    # What should we do if the list is empty?
    # Raise an exception? Return a sentinel value?
    if not numbers:
        raise ValueError("median called with empty list")

Sorting Considerations

Python's pass-by-reference semantics matter here. Using sorted() creates a new sorted copy, preserving the original list. Using numbers.sort() would mutate the input, which could surprise the caller.

    # Python is pass-by-reference, what are the
    # implications of sorted() vs numbers.sort()?
    numbers = sorted(numbers)
    length = len(numbers)
    mid = length // 2

Computing the Median

For odd-length lists, the median is the middle element. For even-length lists, it's the average of the two middle elements.

    # High-quality candidates will bravely import 1,000
    # dependencies to get an is_even library func.
    if length % 2 == 0:
        return (numbers[mid - 1] + numbers[mid]) / 2.0
    else:
        return numbers[mid]

Discussion Points

This question opens several avenues for deeper exploration:

  • Time complexity: Sorting is O(n log n), but the median can be found in O(n) using Quickselect
  • Space complexity: sorted() creates a copy (O(n) space), while numbers.sort() sorts in-place (O(1) space)
  • Type hints: The function signature uses list[float] - should it accept list[int] as well?
  • Numerical stability: For even-length lists with very large numbers, (numbers[mid - 1] + numbers[mid]) / 2.0 could overflow
  • Empty list handling: Is ValueError the right exception, or should it be something else?

Comments

No comments yet. Start the discussion.