leetcode 480.滑动窗口中位数

leetcode 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]。

提示:
你可以假设 k 始终有效,即:k 始终小于输入的非空数组的元素个数。
与真实值误差在 10 ^ -5 以内的答案将被视作正确答案。

思路

容易想到的思路是维护窗口内的数据使其有序,然后查找中位数即可,但显然排序+遍历的解法效率过低。
我们需要一种合适的数据结构,其内部元素有序,且能提供一种更高效的查找方法。
较好的解法是手撸支持重复元素的BST(平衡二叉搜索树),并实现根据位置(比如在k大的窗口中第3位,按照第3位这个数据)查找元素。

题解

也可以调用c++的扩展库pbds来实现红黑树
关于详细的用法可以参考链接
由于红黑树不支持重复元素,所以我们将树中的数据设为pair,同时nums数组中的下标作为标识符来避免这个问题。
tree的find_by_order方法会返回迭代器,优化了寻找窗口内中位数的效率。

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

using namespace __gnu_pbds;

class Solution {
public:
    vector<double> medianSlidingWindow(vector<int>& nums, int k) {
        //null_type无映射,less根据pair的第一个元素进行从小到大排列作为tree的策略,rb_tree_tag实现红黑树,tree_order_statistics_node_update更新节点
        tree<std::pair<int, int>,null_type,std::less<std::pair<int, int>>,rb_tree_tag,tree_order_statistics_node_update> rbTree;
        std::vector<double> ans;
        std::queue<std::pair<int,int>> window;
        int n = nums.size();

        for(int i = 0 ; i < n ; ++i){
            window.push({nums[i],i});
            rbTree.insert({nums[i],i});
            if(window.size() == k){
                double midianPublic = (*rbTree.find_by_order(k / 2)).first;
                double midianEven = (*rbTree.find_by_order(k / 2 - 1)).first;
                double midian = k % 2 == 1 ? midianPublic : (midianPublic + midianEven) / 2;
                ans.push_back(midian);
                rbTree.erase(window.front());
                window.pop();
            }
        }
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值