Skip to main content

Sliding Window Median


Sliding Window Median: The median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle values.

  • For examples, if arr = [2,3,4], the median is 3.
  • For examples, if arr = [1,2,3,4], the median is (2 + 3) / 2 = 2.5.

You are given an integer array nums and an integer k. There is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position.

Return the median array for each window in the original array. Answers within 10^-5 of the actual value will be accepted.

Example 1:
Input: nums = [1,3,-1,-3,5,3,6,7], k = 3
Output: [1.00000,-1.00000,-1.00000,3.00000,5.00000,6.00000]
Explanation:
Window position Median
--------------- -----
[1 3 -1] -3 5 3 6 7 1
1 [3 -1 -3] 5 3 6 7 -1
1 3 [-1 -3 5] 3 6 7 -1
1 3 -1 [-3 5 3] 6 7 3
1 3 -1 -3 [5 3 6] 7 5
1 3 -1 -3 5 [3 6 7] 6

Example 2:
Input: nums = [1,2,3,4,2,3,1,4,2], k = 3
Output: [2.00000,3.00000,3.00000,3.00000,2.00000,3.00000,2.00000]

Constraints:
  • 1 <= k <= nums.length <= 10^5
  • -2^31 <= nums[i] <= 2^31 - 1

Try this Problem on your own or check similar problems:

  1. Find Median from Data Stream
Solution:
class Solution {
public double[] medianSlidingWindow(int[] nums, int k) {
double[] result = new double[nums.length - k + 1];
TreeSet<Integer> max = new TreeSet<>((a, b) -> nums[b] == nums[a] ? a - b : Integer.compare(nums[b], nums[a]));
TreeSet<Integer> min = new TreeSet<>((a, b) -> nums[b] == nums[a] ? a - b : Integer.compare(nums[a], nums[b]));

for(int i = 0; i < nums.length; ++i){
if(i - k >= 0){
if (!max.remove(i - k)) {
min.remove(i - k);
}
}

max.add(i);
balanceTrees(nums, max, min);

if (i - k + 1 >= 0) {
result[i - k + 1] = getMedian(nums, k, max, min);
}
}

return result;
}

private void balanceTrees(int[] nums, TreeSet<Integer> max, TreeSet<Integer> min){
if(max.size() > min.size() + 1){
min.add(max.pollFirst());
}

if(!min.isEmpty() && nums[min.first()] < nums[max.first()]){
int temp = min.pollFirst();
min.add(max.pollFirst());
max.add(temp);
}
}
private double getMedian(int[] nums, int k, TreeSet<Integer> max, TreeSet<Integer> min){
if(max.size() > min.size()) return (double) nums[max.first()];
return ((double)nums[max.first()] + nums[min.first()]) / 2.0;
}
}

Time/Space Complexity:
  • Time Complexity: O(nlogk)
  • Space Complexity: O(k)

Explanation:

The solution is actually combination of implementations of Find Median from Data Stream (where we have utilized balancing method and extract it to a method balanceTrees and getMedian to retrieve the median using two heaps) and Sliding Window Maximum (where we have utilized the discard index method to remove indices that are out of window when shifting the window to the right, and also the approach to fill in nums.length - k + 1 array). There are few traps to take note on:

  • First we should use Integer.compare(nums[b], nums[a]) instead of nums[b]-nums[a] since allows us to cope with Integer.MIN_VALUE and Intger.MAX_VALUE which are allowed in input array by the problem constraints
  • When balancing the heaps, we should compare its value in nums not their raw value since we're dealing with indices not the real array value inside heaps
  • We're using TreeSet since they enable us to remove/random access in O(logk)
  • One thing to note is that TreeSet are sensitive to duplicates (since they're sets, they will only store one occurrence of each repeating element), to cope with this we use indices instead of real array value.

The time complexity is time required to traverse the input array each time doing TreeSet operations O(logk) leading to O(nlogk) with space complexity equal to the number of elements we hold in TreeSet which can be at most the size of window k.