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: 1 ≤ k ≤ input array's size for non-empty array.

这道题给了我们一个数组,还是滑动窗口的大小,让我们求滑动窗口的中位数。我想起来之前也有一道滑动窗口的题Sliding Window Maximum,于是想套用那道题的方法,可以用deque怎么也做不出,因为求中位数并不是像求最大值那样只操作deque的首尾元素。后来看到了史蒂芬大神的方法,原来是要用一个multiset集合,和一个指向最中间元素的iterator。我们首先将数组的前k个数组加入集合中,由于multiset自带排序功能,所以我们通过k/2能快速的找到指向最中间的数字的迭代器mid,如果k为奇数,那么mid指向的数字就是中位数;如果k为偶数,那么mid指向的数跟前面那个数求平均值就是中位数。当我们添加新的数字到集合中,multiset会根据新数字的大小加到正确的位置,然后我们看如果这个新加入的数字比之前的mid指向的数小,那么中位数肯定被拉低了,所以mid往前移动一个,再看如果要删掉的数小于等于mid指向的数(注意这里加等号是因为要删的数可能就是mid指向的数),则mid向后移动一个。然后我们将滑动窗口最左边的数删掉,我们不能直接根据值来用erase来删数字,因为这样有可能删掉多个相同的数字,而是应该用lower_bound来找到第一个不小于目标值的数,通过iterator来删掉确定的一个数字,参见代码如下:

解法一:

class Solution {
public:
vector<double> medianSlidingWindow(vector<int>& nums, int k) {
vector<double> res;
multiset<double> ms(nums.begin(), nums.begin() + k);
auto mid = next(ms.begin(), k / );
for (int i = k; ; ++i) {
res.push_back((*mid + *prev(mid, - k % )) / );
if (i == nums.size()) return res;
ms.insert(nums[i]);
if (nums[i] < *mid) --mid;
if (nums[i - k] <= *mid) ++mid;
ms.erase(ms.lower_bound(nums[i - k]));
}
}
};

上面的方法用到了很多STL内置的函数,比如next,lower_bound啥的,下面我们来看一种不使用这些函数的解法。这种解法跟Find Median from Data Stream那题的解法很类似,都是维护了small和large两个堆,分别保存有序数组的左半段和右半段的数字,保持small的长度大于等于large的长度。我们开始遍历数组nums,如果i>=k,说明此时滑动窗口已经满k个了,再滑动就要删掉最左值了,我们分别在small和large中查找最左值,有的话就删掉。然后处理增加数字的情况(分两种情况:1.如果small的长度小于large的长度,再看如果large是空或者新加的数小于等于large的首元素,我们把此数加入small中。否则就把large的首元素移出并加入small中,然后把新数字加入large。2.如果small的长度大于large,再看如果新数字大于small的尾元素,那么新数字加入large中,否则就把small的尾元素移出并加入large中,把新数字加入small中)。最后我们再计算中位数并加入结果res中,根据k的奇偶性来分别处理,参见代码如下:

解法二:

class Solution {
public:
vector<double> medianSlidingWindow(vector<int>& nums, int k) {
vector<double> res;
multiset<int> small, large;
for (int i = ; i < nums.size(); ++i) {
if (i >= k) {
if (small.count(nums[i - k])) small.erase(small.find(nums[i - k]));
else if (large.count(nums[i - k])) large.erase(large.find(nums[i - k]));
}
if (small.size() <= large.size()) {
if (large.empty() || nums[i] <= *large.begin()) small.insert(nums[i]);
else {
small.insert(*large.begin());
large.erase(large.begin());
large.insert(nums[i]);
}
} else {
if (nums[i] >= *small.rbegin()) large.insert(nums[i]);
else {
large.insert(*small.rbegin());
small.erase(--small.end());
small.insert(nums[i]);
}
}
if (i >= (k - )) {
if (k % ) res.push_back(*small.rbegin());
else res.push_back(((double)*small.rbegin() + *large.begin()) / );
}
}
return res;
}
};

类似题目:

Find Median from Data Stream

Sliding Window Maximum

参考资料:

https://discuss.leetcode.com/topic/74905/c-o-n-logk-using-two-std-set

https://discuss.leetcode.com/topic/75160/easy-to-understand-clean-java-code

https://discuss.leetcode.com/topic/74963/o-n-log-k-c-using-multiset-and-updating-middle-iterator

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Sliding Window Median 滑动窗口中位数的更多相关文章

  1. 480 Sliding Window Median 滑动窗口中位数

    详见:https://leetcode.com/problems/sliding-window-median/description/ C++: class Solution { public: ve ...

  2. [LeetCode] Sliding Window Maximum 滑动窗口最大值

    Given an array nums, there is a sliding window of size k which is moving from the very left of the a ...

  3. [LeetCode] 239. Sliding Window Maximum 滑动窗口最大值

    Given an array nums, there is a sliding window of size k which is moving from the very left of the a ...

  4. [leetcode]239. Sliding Window Maximum滑动窗口最大值

    Given an array nums, there is a sliding window of size k which is moving from the very left of the a ...

  5. POJ - 2823 Sliding Window (滑动窗口入门)

    An array of size n ≤ 10 6 is given to you. There is a sliding window of size kwhich is moving from t ...

  6. Sliding Window(滑动窗口)

    Time Limit: 12000MS   Memory Limit: 65536K Total Submissions: 58002   Accepted: 16616 Case Time Limi ...

  7. Leetcode: Sliding Window Median

    Median is the middle value in an ordered integer list. If the size of the list is even, there is no ...

  8. 239 Sliding Window Maximum 滑动窗口最大值

    给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧.你只可以看到在滑动窗口 k 内的数字.滑动窗口每次只向右移动一位.例如,给定 nums = [1,3,-1,-3, ...

  9. POJ 2823 Sliding Window (滑动窗口的最值问题 )

    Sliding Window Time Limit: 12000MS   Memory Limit: 65536K Total Submissions: 41264   Accepted: 12229 ...

随机推荐

  1. [git 实践篇]如何创建公钥

    如何创建公钥 首先启动一个Git Bash窗口(非Windows用户直接打开终端) 执行: cd ~/.ssh 如果返回"- No such file or directory", ...

  2. oracle数据库--序列(sequence)

    一个问题: 在某张表中,存在一个id列(整数),我们希望在添加记录的时候,该列从1开始,自动的增长,怎么处理? 解决方式:oracle是利用"序列"(sequence)来完成的. ...

  3. OC实现单选和多选按钮

    本代码库暂时有OC封装,改天有空在补一个Swift封装的,主要是因为swift不是那么熟,怕出错,半天找不到问题多尴尬呀! 先附上demo下载地址CSDN:http://download.csdn.n ...

  4. Python中安装模块的方法

    1.*nix系统上有一个地方专门有一个地方来放置安装的Python模块 比如在Mac上,这个目录的路径为: /usr/lib/python2.7 将要安装的文件拷贝到这里即可 2.下载模块包,解压后, ...

  5. 云计算--网络原理与应用--20171122--STP与HSRP

    简单了解STP 学习HSRP 实验 一.  简单学习STP STP(spanning tree protocol)生成树协议,就是把一个环形的结构改变成一个树形的结构.通过一些算法,在逻辑上阻塞一些端 ...

  6. 云计算之路-阿里云上-容器难容:优化自建 docker swarm 集群的部署

    在上周六遭遇阿里云容器服务 swarm 版的故障之后,我们决定还是走自建 docker swarm 之路,只要不是阿里云底层的问题,我们相信会找到办法解决或避开自建 docker swarm 不稳定的 ...

  7. Alpha冲刺——Day2

    一.合照 二.项目燃尽图 三.项目进展 图形界面基本完成 接口文档框架完成,接下来将会不断细化填充 登录界面向服务器请求数据进行ing 四.明日规划 1.注册登录接口能够完成 2.研究idea实现获得 ...

  8. display属性

    display 属性规定元素应该生成的框的类型. 值 描述 none 此元素不会被显示. block 此元素将显示为块级元素,此元素前后会带有换行符. inline 默认.此元素会被显示为内联元素,元 ...

  9. Connect Appium Server Fail.A new session could not be created

    1.由于安卓测试机性能低下,并不能支持测试工作,想安装一个模拟器帮助测试,然后发现群里有朋友发了一个夜神模拟器..下载..安装..美滋滋的准备运行脚本.What..居然报错了..orz..然后百度查找 ...

  10. EasyUI datagrid 使用小结

    用了 EasyUI 框架一段时间了,这个前端框架用起来还是挺方便的,也有很多现成的控件,看看官方文档应该还是能比较快用起来的. 在这里记录一下一些常用的控件的方法,遇到过的bug或者当初耗了一点时间来 ...