LintCode.360.滑动窗口的中位数

本文介绍了一种计算滑动窗口中位数的有效方法。通过使用两个multiset实现动态平衡,可在数组上高效地计算不同窗口大小的中位数。文章对比了两种算法:一种是简单的枚举方法,另一种则是利用数据结构优化后的解决方案。
摘要由CSDN通过智能技术生成
/** LintCode.360.滑动窗口的中位数
 * 题目:
 *      给定一个包含 n 个整数的数组,和一个大小为 k 的滑动窗口,从左到右在数组中滑动这个窗口,
 *      找到数组中每个窗口内的中位数。(如果数组个数是偶数,则在该窗口排序数字后,返回第 N/2 个数字。)
 * 样例:
 *      对于数组 [1,2,7,8,5], 滑动大小 k = 3 的窗口时,返回 [2,7,7]
 * 思路:
 *  方案一: 最容易想到的方案是枚举,按顺序枚举每个滑动窗口的位置,计算出答案,为了修改的方便,
 *         尽量将可能会改进的地方封装起来。
 *      结果: TLE
 *  方案二: 方案一由于过分追求松耦合导致运行开销过大。如果有一种数据结构能够像队列一样先进先出,又能像平衡二叉树一样
 *         根节点即为中位数就好了。由于我们能够在O(1)时间找到该出队元素,因此可以使用multiset.
 *      结果: Ac
 */

// 方案一: Time Limit Exceeded
class Solution
{
public:
    /*
     * @param nums: A list of integers
     * @param k: An integer
     * @return: The median of the element inside the window at each moving
     */
    vector<int> medianSlidingWindow(vector<int> &nums, int k)
    {
        if (nums.empty() || k <= 0)
            return {};

        vector<int> result;
        for (size_t i = 0; i + k <= nums.size(); ++i)
        {
            int leftElements = nums.size() - i; // 剩余元素个数
            int selectedElements = min(k, leftElements); // 本次窗口中的元素个数
            int median = GetMedian(nums.data() + i, selectedElements); // 获取窗口中的中位数
            result.push_back(median);
        }

        return result;
    }

    int GetMedian(const int* window, int size)
    {
        int arr[size];
        memcpy(arr, window, size * sizeof(*arr));
        sort(arr, arr + size);
        return arr[size-1 >> 1];
    }
};

// 方案二: Accepted
class Solution
{
  public:
    /*
     * @param nums: A list of integers
     * @param k: An integer
     * @return: The median of the element inside the window at each moving
     */
    vector<int> medianSlidingWindow(vector<int> &nums, int k)
    {
        if (nums.empty() || k <= 0 || k > nums.size())
            return {};

        vector<int> result;
        multiset<int> min_top_heap; // 4,5,6
        multiset<int, greater<int> > max_top_heap; // 3,2,1
        for (size_t i = 0; i < nums.size(); ++i)
        {
            // 插入
            max_top_heap.insert(nums[i]);
            int cur_window_size = max_top_heap.size() + min_top_heap.size();
            // 调整
            /// 移动
            if (max_top_heap.size() > min_top_heap.size() + 1)
            {
                auto key_it = max_top_heap.begin();
                min_top_heap.insert(*key_it);
                max_top_heap.erase(key_it);
            }
            /// 交换
            if (min_top_heap.size() && *max_top_heap.begin() > *min_top_heap.begin())
            {
                int max_top_val = *max_top_heap.begin();
                int min_top_val = *min_top_heap.begin();
                max_top_heap.erase(max_top_heap.begin());
                min_top_heap.erase(min_top_heap.begin());
                max_top_heap.insert(min_top_val);
                min_top_heap.insert(max_top_val);
            }
            // 生成结果
            if (cur_window_size == k)
            {
                result.push_back(*max_top_heap.begin());
                /// 出队
                int out_element = nums[i-k+1];
                auto min_key_it = min_top_heap.find(out_element);
                if (min_key_it != min_top_heap.end())
                {
                    min_top_heap.erase(min_key_it);
                }
                else
                {
                    auto max_key_it = max_top_heap.find(out_element);
                    if (max_key_it != max_top_heap.end())
                        max_top_heap.erase(max_key_it);
                }
            }
        }

        return result;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值