列表

详情


NC395. 滑动窗口中位数

描述

给定一个长度为 n 的正整数数组,和一个窗口长度 k ,有一个长度为 k 的窗口从最左端滑到最右端。请你算出所有窗口的中位数。
中位数是有序数组最中间的数,如果序列长度是偶数,中位数就是两个最中间的数的平均数。

数据范围:数组长度满足 ,数组中的值满足

示例1

输入:

[4,5,9,7,8,5],3

输出:

[5,7,8,7]

示例2

输入:

[4,5,9,7,8,5],4

输出:

[6,7.5,7.5]

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++ 解法, 执行用时: 76ms, 内存消耗: 7728KB, 提交时间: 2022-05-25

class DualHeap {
private:
    // 大根堆,维护较小的一半元素
    priority_queue<int> small;
    // 小根堆,维护较大的一半元素
    priority_queue<int, vector<int>, greater<int>> large;
    // 哈希表,记录「延迟删除」的元素,key 为元素,value 为需要删除的次数
    unordered_map<int, int> delayed;

    int k;
    // small 和 large 当前包含的元素个数,需要扣除被「延迟删除」的元素
    int smallSize, largeSize;

public:
    DualHeap(int _k): k(_k), smallSize(0), largeSize(0) {}

private:
    // 不断地弹出 heap 的堆顶元素,并且更新哈希表
    template<typename T>
    void prune(T& heap) {
        while (!heap.empty()) {
            int num = heap.top();
            if (delayed.count(num)) {
                --delayed[num];
                if (!delayed[num]) {
                    delayed.erase(num);
                }
                heap.pop();
            }
            else {
                break;
            }
        }
    }

    // 调整 small 和 large 中的元素个数,使得二者的元素个数满足要求
    void makeBalance() {
        if (smallSize > largeSize + 1) {
            // small 比 large 元素多 2 个
            large.push(small.top());
            small.pop();
            --smallSize;
            ++largeSize;
            // small 堆顶元素被移除,需要进行 prune
            prune(small);
        }
        else if (smallSize < largeSize) {
            // large 比 small 元素多 1 个
            small.push(large.top());
            large.pop();
            ++smallSize;
            --largeSize;
            // large 堆顶元素被移除,需要进行 prune
            prune(large);
        }
    }

public:
    void insert(int num) {
        if (small.empty() || num <= small.top()) {
            small.push(num);
            ++smallSize;
        }
        else {
            large.push(num);
            ++largeSize;
        }
        makeBalance();
    }

    void erase(int num) {
        ++delayed[num];
        if (num <= small.top()) {
            --smallSize;
            if (num == small.top()) {
                prune(small);
            }
        }
        else {
            --largeSize;
            if (num == large.top()) {
                prune(large);
            }
        }
        makeBalance();
    }

    double getMedian() {
        return k & 1 ? small.top() : ((double)small.top() + large.top()) / 2;
    }
};

class Solution {
public:
    vector<double> slidewindow(vector<int>& nums, int k) {
        DualHeap dh(k);
        for (int i = 0; i < k; ++i) {
            dh.insert(nums[i]);
        }
        vector<double> ans = {dh.getMedian()};
        for (int i = k; i < nums.size(); ++i) {
            dh.insert(nums[i]);
            dh.erase(nums[i - k]);
            ans.push_back(dh.getMedian());
        }
        return ans;
    }
};

C++ 解法, 执行用时: 85ms, 内存消耗: 5820KB, 提交时间: 2022-07-20

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

C++ 解法, 执行用时: 85ms, 内存消耗: 5820KB, 提交时间: 2022-05-19

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型vector 
     * @param k int整型 
     * @return double浮点型vector
     */
    vector<double> slidewindow(vector<int>& nums, int k) {
        // write code here
        vector<double> result;
        multiset<int> window(nums.begin(), nums.begin() + k);
        auto mid = next(window.begin(), k / 2);
        for (int i = k; ; i++) 
        {
            result.push_back((double(*mid) + *prev(mid, 1 - k % 2)) / 2);
            if (i == nums.size()) 
                return result;
            window.insert(nums[i]);
            if (nums[i] < *mid) 
                mid--;
            if (nums[i - k] <= *mid) 
                mid++;
            window.erase(window.lower_bound(nums[i - k]));
        }
    }
};

C++ 解法, 执行用时: 88ms, 内存消耗: 5916KB, 提交时间: 2022-08-06

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型vector 
     * @param k int整型 
     * @return double浮点型vector
     */
    vector<double> slidewindow(vector<int>& nums, int k) {
        // write code here
        vector<double> v;
        multiset<int> t(nums.begin(),nums.begin()+k);
        auto f=next(t.begin(),k/2);
        for(int i=k;;i++)
        {
            v.push_back((double(*f)+*prev(f,1-k%2))/2);
            if(i==nums.size())
                return v;
            t.insert(nums[i]);
            if(nums[i]<*f)
                f--;
            if(nums[i-k]<=*f)
                f++;
            t.erase(t.lower_bound(nums[i-k]));
        }
    }
};

C++ 解法, 执行用时: 91ms, 内存消耗: 7732KB, 提交时间: 2022-04-22

class Solution {
  public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param nums int整型vector
     * @param k int整型
     * @return double浮点型vector
     */
    /*int indexOf(vector<int>& src, const int target) {
        int result = -1;
        for (int i = 0; i < src.size(); i++) {
            if (src[i] == target) {
                result = i;
                break;
            }
        }

        return result;
    }

    void setBottleVisited(vector<int>& sorted_array, vector<bool>& bottle,
                          int target) {
        int i = 0;
        while (i < bottle.size()) {
            if (sorted_array[i] == target && !bottle[i]) {
                bottle[i] = true;
                return;
            }
            i++;
        }
    }

    //现在版本中的题解网站:https://www.bilibili.com/video/BV1dv4y1o7Un?spm_id_from=333.337.search-card.all.click
    //这题在力扣上用这种类暴力题解是能通过的,牛客上通过不了,如果正确的方法要用
    //优先队列双堆,一个large一个small来控制
    vector<double> slidewindow(vector<int>& nums, int k) {
        // write code here
        if (nums.size() < k) {
            return vector<double>();
        }

        vector<double> result;

        vector<int> sorted_array = nums;
        sort(sorted_array.begin(), sorted_array.end());
        vector<bool> bottle(nums.size(), false);

        int left = 0, right = k - 1;
        //刚开始的时候先把[left,right-1]的所有元素对应的下标置为true
        for (int i = left; i < right; i++) {
            int targetIndex = indexOf(sorted_array, nums[i]);
            //这里要注意:找到的targetIndex可能是已经被置为true了,因此要接下来往后移动一个进行查找
            while(bottle[targetIndex]){
                targetIndex++;
            }
            bottle[targetIndex] = true;
        }


        while (right < nums.size()) {
            //当前滑动窗口内新的元素right在其中置为true
            int rightIndexInSortedArray = indexOf(sorted_array, nums[right]);
            while(bottle[rightIndexInSortedArray]){
                rightIndexInSortedArray++;
            }
            bottle[rightIndexInSortedArray] = true;

            //找中位数
            if (k % 2 == 1) {
                int i = 0;
                int cnt = 0;
                while (i < bottle.size()) {
                    if (bottle[i]) {
                        cnt++;
                    }

                    if (cnt == (k / 2 + 1)) {
                        result.push_back(sorted_array[i]);
                        break;
                    }

                    i++;
                }
            } else {
                double sum = 0;
                int i = 0;
                int cnt = 0;
                while (i < bottle.size()) {
                    if (bottle[i]) {
                        cnt++;

                        if (cnt == (k / 2)) {
                            sum = sum + sorted_array[i];
                        } else if (cnt == (k / 2 + 1)) {
                            sum = sum + sorted_array[i];
                            break;
                        }
                    }
                    i++;
                }

                result.push_back(sum * 1.0 / 2);
            }

            //清空left指向的这个数,因为下一次用不到了
            int leftIndexInSorted = indexOf(sorted_array, nums[left]);
            while(!bottle[leftIndexInSorted]){
                leftIndexInSorted++;
            }
            bottle[leftIndexInSorted] = false;

            left++;
            right++;
        }

        return result;
    }*/

    //真正的题解
    //网站:https://blog.csdn.net/weixin_43601103/article/details/113587918
    priority_queue<int> small;
    priority_queue<int, vector<int>, greater<int>> large;
    int smallSize = 0, largeSize = 0;
    unordered_map<int, int> ma;

    template<typename T>
    void prune(T& heap) {
        while (!heap.empty()) {
            int x = heap.top();
            if (!ma.count(x)) break;
            ma[x]--;
            if (ma[x] == 0) {
                ma.erase(x);
            }
            heap.pop();
        }
    }

    void makeBalance() {
        if (smallSize >= largeSize + 2) {
            large.push(small.top());
            small.pop();
            smallSize--;
            largeSize++;
            prune(small);
        } else if (smallSize < largeSize) {
            small.push(large.top());
            large.pop();
            smallSize++;
            largeSize--;
            prune(large);
        }
    }

    void insert(int x) {
        if (small.empty() || x <= small.top()) {
            small.push(x);
            smallSize++;
        } else {
            large.push(x);
            largeSize++;
        }
        makeBalance();
    }

    void Delete(int x) {
        ma[x]++;
        if (x <= small.top()) {
            smallSize--;
            if (x == small.top()) {
                prune(small);
            }
        } else {
            largeSize--;
            if (x == large.top()) {
                prune(large);
            }
        }
        makeBalance();
    }
    double getMedian(int k) {
        return k & 1 ? small.top() : ((double) small.top() + large.top()) / 2;
    }

    vector<double> slidewindow(vector<int>& nums, int k) {
        int n = nums.size();
        for (int i = 0; i < k; i++) {
            insert(nums[i]);
        }
        vector<double> res = {getMedian(k)};
        for (int i = k; i < n; i++) {
            insert(nums[i]);
            Delete(nums[i - k]);
            res.push_back(getMedian(k));
        }
        return res;
    }
};

上一题