480. 滑动窗口中位数 ( 有序数组+滑动窗口)
难度困难
中位数是有序序列最中间的那个数。如果序列的长度是偶数,则没有最中间的数;此时中位数是最中间的两个数的平均数。
例如:
[2,3,4]
,中位数是3
[2,3]
,中位数是(2 + 3) / 2 = 2.5
给你一个数组 nums,有一个长度为 k 的窗口从最左端滑动到最右端。窗口中有 k 个数,每次窗口向右移动 1 位。你的任务是找出每次窗口移动后得到的新窗口中元素的中位数,并输出由它们组成的数组。
示例:
给出 nums = [1,3,-1,-3,5,3,6,7]
,以及 k = 3。
窗口位置 中位数 --------------- ----- [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
因此,返回该滑动窗口的中位数数组 [1,-1,-1,3,5,6]
。
from sortedcontainers import SortedList class Solution: def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]: def get_median(window): n = len(window) if n % 2 == 1: return window[n // 2] else: return (window[n // 2 - 1] + window[n // 2]) / 2 st = SortedList() ans = [] for i in range(len(nums)): #这个条件用于确保我们已经处理了至少 k 个元素后,窗口才开始滑动。 if i >= k: st.remove(nums[i - k]) st.add(nums[i]) #只有当窗口的大小达到了 k 之后,我们才能开始计算中位数 if i >= k - 1: median = get_median(st) ans.append(median) return ans
class Solution { public: vector<double> medianSlidingWindow(vector<int>& nums, int k) { vector<double> res; for(int i = 0; i <= nums.size()-k;i++) { vector<long> temp(nums.begin()+i,nums.begin()+i+k); //开区间 sort(temp.begin(),temp.end()); double a = k%2!=0?temp[k/2]: double(temp[k/2]+temp[k/2-1])/2.0; res.emplace_back(a); } return res; } };
class Solution { public: vector<double> medianSlidingWindow(vector<int>& nums, int k) { vector<double> ret; multiset<int> window(nums.begin(), nums.begin() + k); // 初始化set,将前k个数存入set auto mid = std::next(window.begin(), k / 2); // 基数:获取中数的位置,偶数:获取中数计数参数的后一个数的位置 for (int idx = k;idx < nums.size() ; idx++) { // 1、计数每组窗口中数组的中数,并存入ret中 auto midValue = ((double)(*mid) + (double)*next(mid, k % 2 - 1)) / 2; ret.push_back(midValue); // 遍历完了,退出 if (idx == nums.size()) { break; } // 窗口后移 window.insert(nums[idx]); if (nums[idx] < *mid) mid--; // same as mid = prev(mid) // Remove outgoing element if (nums[idx - k] <= *mid) mid++; // same as mid = next(mid) // 删除一个元素 window.erase(window.lower_bound(nums[idx-k])); } return ret; } };