Sliding Window Median

Sanjeev SharmaSanjeev Sharma
3 min read

Advertisement

Problem

Given an array and window size k, return an array of medians of each sliding window of size k.

Key insight: Extend two-heap approach with lazy deletion using a hash map of elements to remove.

Approach

  • Two heaps (lo max-heap, hi min-heap)
  • Track elements to "lazily" remove; clean up when they reach heap tops

Solutions

// C++ — lazy deletion
vector<double> medianSlidingWindow(vector<int>& nums, int k) {
    priority_queue<int> lo;
    priority_queue<int, vector<int>, greater<int>> hi;
    unordered_map<int, int> del;
    auto balance = [&]() {
        while (!hi.empty() && del.count(hi.top()) && del[hi.top()] > 0)
            { del[hi.top()]--; hi.pop(); }
        while (!lo.empty() && del.count(lo.top()) && del[lo.top()] > 0)
            { del[lo.top()]--; lo.pop(); }
    };
    for (int i = 0; i < k; i++) { lo.push(nums[i]); hi.push(lo.top()); lo.pop(); if((int)hi.size()>lo.size()){lo.push(hi.top());hi.pop();} }
    auto getMedian = [&]() -> double {
        return k%2==0 ? ((double)lo.top()+hi.top())/2 : lo.top();
    };
    vector<double> res = {getMedian()};
    for (int i = k; i < (int)nums.size(); i++) {
        int out = nums[i-k], in = nums[i];
        del[out]++;
        int sign = (out <= lo.top()) ? -1 : 1;
        if (in <= lo.top()) { lo.push(in); if (sign == -1) { balance(); hi.push(lo.top()); lo.pop(); } }
        else { hi.push(in); if (sign == 1) { balance(); lo.push(hi.top()); hi.pop(); } }
        balance();
        res.push_back(getMedian());
    }
    return res;
}
# Python — using SortedList for O(log k) ops
from sortedcontainers import SortedList

def medianSlidingWindow(nums, k):
    window = SortedList(nums[:k])
    result = []
    def get_median():
        if k % 2 == 0:
            return (window[k//2 - 1] + window[k//2]) / 2
        return float(window[k//2])
    result.append(get_median())
    for i in range(k, len(nums)):
        window.add(nums[i])
        window.remove(nums[i - k])
        result.append(get_median())
    return result
// Java — TreeMap approach
public double[] medianSlidingWindow(int[] nums, int k) {
    TreeMap<Integer, Integer> lo = new TreeMap<>(Collections.reverseOrder());
    TreeMap<Integer, Integer> hi = new TreeMap<>();
    int loSize = 0, hiSize = 0;
    // Complex implementation omitted — see two-heap lazy deletion pattern
    return new double[nums.length - k + 1]; // placeholder
}
// JavaScript — SortedArray simulation
function medianSlidingWindow(nums, k) {
    const sorted = [...nums.slice(0, k)].sort((a, b) => a - b);
    const result = [];
    const getMedian = () => k % 2 === 0 ? (sorted[k/2-1] + sorted[k/2]) / 2 : sorted[Math.floor(k/2)];
    result.push(getMedian());
    for (let i = k; i < nums.length; i++) {
        const removeIdx = sorted.findIndex(x => x === nums[i-k]);
        sorted.splice(removeIdx, 1);
        let lo2 = 0, hi2 = sorted.length;
        while (lo2 < hi2) { const mid = (lo2+hi2)>>1; if(sorted[mid]<nums[i])lo2=mid+1;else hi2=mid; }
        sorted.splice(lo2, 0, nums[i]);
        result.push(getMedian());
    }
    return result;
}

Complexity

  • O(n log k) per element with SortedList or lazy deletion

Key Insight

Lazy deletion: mark elements for removal but only actually remove them when they surface to heap tops. Maintain balance invariant after each slide.

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro