LintCode 360: Sliding Window Median (双堆 或 双Set, 难题!)

  1. Sliding Window Median
    Given an array of n integer, and a moving window(size k), move the window at each iteration from the start of the array, find the median of the element inside the window at each moving. (If there are even numbers in the array, return the N/2-th number after sorting the element in the window. )

Example
For array [1,2,7,8,5], moving window size k = 3. return [2,7,7]

At first the window is at the start of the array like this

[ | 1,2,7 | ,8,5] , return the median 2;

then the window move one step forward.

[1, | 2,7,8 | ,5], return the median 7;

then the window move one step forward again.

[1,2, | 7,8,5 | ], return the median 7;

Challenge
O(nlog(n)) time

思路:
用双堆maxHeap或minHeap,分别装k/2左右的元素。同时维护一个sliding window,保证两个heap的总元素个数是K个。median就是minHeap的最后一个元素。

注意:

  1. JAVA的priorityqueue有erase(),但C++的priority_queue不提供erase()这个函数。所以只能用set。而有可能存在重复元素,故用multiset。
  2. 删除元素的时候要用erase(iterator)。因为虽然erase()也可以用元素值做参数,但有重复元素的话就把所有的重复元素都删掉了。
  3. set.end()是最后一个元素还往后一个,所以要得到set()的最后一个元素必须用set.rbegin(),好像也可以用–set.end()。但erase()的参数必须是值或iterator,所以不能用set.rbegin(),因为是reverse iterator。

代码如下:

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) {
            
        int len = nums.size();
        if ((len < k) || (k == 0)) return vector<int>();
        vector<int> result;
        multiset<int> smallSet, largetSet;
        
        for (int i = 0; i < len; ++i) {
            
            //make sure sum of smallSet.size() and largetSet.size() not exceeds k 
            if (i >= k) {
                //remove nums[i - k], sliding window 
                if (smallSet.find(nums[i - k]) != smallSet.end()) smallSet.erase(smallSet.find(nums[i - k]));
                else largetSet.erase(largetSet.find(nums[i - k]));
            }
            
            if (smallSet.size() <= largetSet.size()) { //try to insert into smallSet first
                if (largetSet.empty() || nums[i] <= *largetSet.begin()) {
                    smallSet.insert(nums[i]);
                } else {
                    smallSet.insert(*largetSet.begin());
                    largetSet.erase(largetSet.begin());
                    largetSet.insert(nums[i]);
                }
            } else { //try to insert into largetSet first
                //use .rbegin() to point to the last element, not .end() !!!   
                if (smallSet.empty() || nums[i] >= *smallSet.rbegin()) {
                    largetSet.insert(nums[i]);
                } else {
                    largetSet.insert(*smallSet.rbegin());
                    smallSet.erase(--smallSet.end());
                    smallSet.insert(nums[i]);
                }
            }
            
            if (i >= k - 1) {
                //int num = (k & 0x1) ? *smallSet.rbegin() : (*smallSet.rbegin() + *largetSet.begin()) / 2;
                result.push_back(*smallSet.rbegin());
            }
            
        }        
        return result;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值