滑动窗口的最大值题解

滑动窗口的最大值

https://www.nowcoder.com/questionTerminal/1624bc35a45c42c0bc17d17fa0cba788

题目

数组num的长度为N,求其所有长度为W的连续区间的最大值。

一、平衡树

直接用map来对每个出现的值计数,map可以直接加入,删除,求最大。
时间复杂度O(NlogW)
空间复杂度O(N)

二、Sparse Table

利用Sparse_Table算法思想,将区间[A, A + W) 分解成[A, A+R) 和[A+W-R, A+W) 其中R是满足 2*R >= W的最小的2的次方。分解成的两个小区间有重叠,但由于是求最值,重叠部分不会影响答案。预处理部分中需要得到从每个下标A开始长度为1,2,4,8,...,R的区间的最值。相比Sparse_Table算法,这里的空间可以优化成O(N),因为只需要保留长度为R的数组。
时间复杂度O(NlogW)
空间复杂度O(N)

三、单调队列

利用这条性质:当A < B且num[A] < num[B]时,num[A]对右端点在B或之后的区间来说可以忽略,因为num[B]是个更优解且num[A]先过期。
于是维护一个单调队列,队列中的元素由高到低排列(队列中存下标,方便判断过期)。
从小到大扫描num数组,当考虑到下标B时,根据上面的性质,可以安全地从队尾删除所有比num[B]小的值。
然后将B加入队尾。
然后从队首删除所有过期的值。
做完以上3点之后,队首的值即为以B为右端点的区间的最大值。
时间和空间复杂度都是O(N),因为每个元素只进出一次队列。
时间复杂度O(N)
空间复杂度O(N)

四、代码

class Solution {
public:
    //平衡树
    vector<int> maxInWindows1(const vector<int>& num,int W)
    {
        int N = num.size();
        vector<int> ret;
        map<int, int> Count;

        for(int i = 0 ; i < N ; ++i){
            //加入当前值
            ++Count[num[i]];
            //删除过期元素
            if(i - W >= 0 && 0 == --Count[num[i - W]])
                Count.erase(num[i - W]);
            //计算答案
            if(i >= W - 1)
                ret.push_back(Count.rbegin()->first);
        }
        return ret;
    }
    //Sparse Table
    vector<int> maxInWindows2(const vector<int>& num,int W)
    {
        int N = num.size();

        vector<int> Max(num.begin(), num.end());
        int MaxRange = 1;
        //Max[i]为区间[i, i + MaxRange)中的最大值
        //每次循环将MaxRange翻倍并保持此性质

        while(MaxRange * 2 < W){
            for(int i = 0 ; i + 2 * MaxRange <= N ; ++i)
                Max[i] = max(Max[i], Max[i+MaxRange]);
            MaxRange *= 2;
        }
        //此时 MaxRange * 2 >= W,即MaxRange至少覆盖半个窗口

        vector<int> ret;
        for(int i = 0 ; i + W <= N; ++i){
            // [i, i + W)被分成[i, i + MaxRange)
            // 和 [i + W - MaxRange, i + W)这两个区间。
            ret.push_back(max(Max[i], Max[i+W-MaxRange]));
        }
        return ret;
    }
    //单调队列
    vector<int> maxInWindows3(const vector<int>& num, int W)
    {
        int N = num.size();

        vector<int> ret;
        list<int> L;
        for(int i = 0 ; i < N ; ++i){
            //从队尾删除比num[i]小的数
            while(!L.empty() && num[*L.rbegin()] < num[i])
                L.pop_back();

            //将i加入队尾
            L.push_back(i);

            //从队首删除过期的数
            while(!L.empty() && (*L.begin() <= i - W))
                L.pop_front();

            //将以i结尾的区间最值加入答案
            if(i >= W - 1)
                ret.push_back(num[*L.begin()]);
        }
        return ret;
    }
};
全部评论
半天没看懂代码为什么能测过,测测发现一个能过的都没有。。。
1 回复 分享
发布于 2020-02-04 15:21
真的,你提供思路ok,但贴代码,贴过不了的代码。。。那还能算题解吗?
1 回复 分享
发布于 2020-04-18 11:43
单调队列本身不是也需要计算么
点赞 回复 分享
发布于 2019-11-05 17:33

相关推荐

从24年初开学开始接触到前端,和实验室几个同学一起学习,可似乎我总比他们慢一步,每每学完一个地方,我掌握的程度好像都不比他们,第一次实验室的任务实战,我两眼一抹黑,完全不知道从何下手,而他们却是游刃有余,可我当时没有丧气,只有一个念头,既然学习能力不如他们,那我就拿更多的时间去学,于是我把打游戏,运动锻炼的时间也拿来学习。到了暑假,实验室一起做项目,为了可以更好的参与进去,于是我暑假开始留校和同学师哥一起做项目,每天早上九点多去实验室,晚上十点多回宿舍,校田径队的训练没有去,中间也只回家待了一周。到暑假结束开学之后,一位很优秀的师哥拿到了几个offer,我从他身上看到了希望,双非本科就业的希望...
offer求求哩:我的评价是认知低,建议多看书,认知低的一个表现是人生仿佛没考上大学就是进厂,考上了就是考研考公找工作。股市里有一个很有意思的故事,说的是当门口大妈都在谈论股票的时候,说明行情已经见顶了。当你的父母在某些事上没有成功却支持你说明事情可能已经不可靠了,但在某些事上反对你,说明这件事可能还有成功的可能。(仅个人观点)😆😆
点赞 评论 收藏
分享
01-07 15:50
四川大学 Java
明远湖摸鱼:同年级的同学,,简历可以大一点,这个有点太密集了,实习技术可以量化的尽量量化
点赞 评论 收藏
分享
MScoding:你这个实习有一个是当辅导老师,这个和找技术岗没有关系吧?
点赞 评论 收藏
分享
评论
10
1
分享

创作者周榜

更多
牛客网
牛客企业服务