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), whilenumbers.sort()sorts in-place (O(1) space) - Type hints: The function signature uses
list[float]- should it acceptlist[int]as well? - Numerical stability: For even-length lists with very large numbers,
(numbers[mid - 1] + numbers[mid]) / 2.0could overflow - Empty list handling: Is
ValueErrorthe right exception, or should it be something else?
Comments
No comments yet. Start the discussion.