代码随想录算法训练营第13天|● 239. 滑动窗口最大值● 347.前 K 个高频元素

239.滑动窗口最大值

解一(暴力解):假设窗口为k,数组大小为n,每次在一个窗口找最大值,遍历n-k次,则时间复杂度O(kn)

优解:时间消耗大的还是在找最大值方面,定义一个单调(从大到小单调减)队列,队列里面仅维护当前窗口可能的最大值。时间复杂度O(n)

单调队列遵循规则:

pop():队列非空,且并且当前value值等于单调队列的队头元素,则弹出队头元素

push(value):value值必须大于队尾元素,才把value加入单调队列,若不是,则一直弹出队尾元素直到队列为空或满足value大于队尾元素.再加入队列。

核心可以理解为:滑动窗口删除(pop)前一个元素,后加入(push)后一个元素可以实现滑动,因为队列仅保存当前窗口可能的最大值(并不是全保存,若当前队列中的值比新加入的元素--push进来的元素还小,则没必要再维护这些小值,因为迟早会被舍弃),数组前窗口删除的元素如果和队头元素相等就应该pop出来,保持队中元素和当前窗口元素的一致性。push则是把后窗口新加入的元素和队中元素比较,把队列中比新加入的元素还小的直接删除。以上就是对该解法比较核心的理解

代码:

class Solution {
    class MyQueue{//重新定义队列方法,单调递减
    public:
        deque que;
        void pop(int value){
            if(!que.empty() && value==que.front()){//仅队列非空且队头元素和窗口最前面的移除值相同才pop
                que.pop_front();
            }
        }
        void push(int value){
            while(!que.empty() && value>que.back()){//仅队列非空且要push的元素比队尾元素大,则把队尾元素移除
                que.pop_back();
            }
            que.push_back(value);//此时push,前面的元素都不会比当前元素大
        }
        int front(){
            return que.front();
        }
    };

public:
    vector maxSlidingWindow(vector& nums, int k) {
        vector result;
        MyQueue que;//队列存放当前窗口可能的最大值
        //1,初始第一个窗口
        for(int i = 0;i < k;i++){//先push数组前k个元素
            que.push(nums[i]);
        }
        result.push_back(que.front());//再统计下当前窗口的最大值
        //2,窗口滑动
        for(int i = k;i < nums.size();i++){
            //i记录尾部位置,i-k记录头部位置
            que.pop(nums[i-k]);//滑动窗口移动前先删除最前面的元素
            que.push(nums[i]);//上面删除最前面的元素后,再加入后面的一个元素,实现滑动
            result.push_back(que.front());//记录当前窗口的最大值
        }
        return result;
    }
};

347.前k个高频元素

优解:可利用map记录该元素的出现次数,再对次数进行排序。时间差异主要出现在排序。要找到前k个高频出现的元素,推荐使用堆排序(又称优先级队列,仅有接口从队头取出元素,队尾插入元素),可以很方便的到结果,

但是对于该题第一时间会想到使用大根堆进行排序,若使用大根堆,当排序元素(出现次数)刚好为k,那么每次都将一个最大值弹出,需要排序弹出k次。并且还需要一个额外的空间去接收弹出的元素.相反使用小根堆,每次把最小的值弹出,最后剩k个元素即为结果,当排序元素仅为k时,就不需要排序直接输出,也不需要额外空间存储结果。

代码:(二刷再总结)

你可能感兴趣的:(数据结构)