leetcode 480. Sliding Window Median

来源:互联网 发布:echarts java 类库 编辑:程序博客网 时间:2024/05/16 02:27

写在前面

又是一道sliding window的题目,实话说,这类型的题目之前遇到的确实不多,可能还需要多加练习。本题在leetcode上被标记为hard,事实上基本思路是比较容易考虑到的,难点在于移动过程中的处理。

题目描述

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 value.

Examples:
[2,3,4] , the median is 3

[2,3], the median is (2 + 3) / 2 = 2.5

Given an array nums, 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. Your job is to output the median array for each window in the original array.

For example,
Given nums = [1,3,-1,-3,5,3,6,7], and k = 3.

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
Therefore, return the median sliding window as [1,-1,-1,3,5,6].

Note:
You may assume k is always valid, ie: k is always smaller than input array’s size for non-empty array.

解题思路

首先set是很容易想到要使用的数据结构,因为C++ set字典序的特性,我们自动就获得了有序的数组,难点在于下标移动过程,leetcode的高票答案先插入后删除的做法避免了删除可能带来的各种问题,保证下标能够正确移动,下面会主要分析这种思路。
这里首先确认使用的数据结构是multiset,保证相同的数也可以插入set。set的大小为k,那么set的中间数字就是要求的median(k为偶数的情况为中间两数的均值)。我们每一轮的求解都是在set中插入新数并删除数组中最左侧元素。问题的关键就是插入和删除的过程。我们只考虑前k/2的部分(这个问题由最开始的中位数位置决定,偶数个的中位数下标认为是后半部分的第一个元素)。假设插入的数据比中位数小,我们便自减下标(先不考虑删除),保证插入后下标的准确性。接着,我们用移动后的下标对应的中位数与要删除的数字比较,如果要删除的数字小于等于新的中位数,我们就递增下标。这里必须注意的是“等于”的限定,因为我们不能够确定要删除的是否是当前的中位数,如果是,则仍需要做递增操作。

根据上面的思路,可以写出如下的代码(其实使用了很多的tricks,主要是思路要清楚,这种代码不大可能在面试的时候一遍写出来):

class Solution {public:    vector<double> medianSlidingWindow(vector<int>& nums, int k) {        // 利用set自排序的特性        multiset<int> mSet(nums.begin(),nums.begin()+k);        vector<double> retVec;        auto mid = next(mSet.begin(),k/2);        for(int i = k;;i++) {            // 插入之前            retVec.push_back((double)((double)*mid +(double)*prev(mid,1-k%2))/2);            if(i==nums.size()) return retVec;            // 插入            mSet.insert(nums[i]);            // 删除            //有玄学啊            if(nums[i]<*mid)                mid--;            if(nums[i-k]<=*mid)                mid++;            mSet.erase(mSet.lower_bound(nums[i-k]));        }    }};
原创粉丝点击