(13/60)滑动窗口最大值、前K个高频元素

滑动窗口最大值

leetcode:239. 滑动窗口最大值

第一个hard!work out!资源占用竟然如此之大,,

0787935dfe6cfe036710c9cf26393c5

单调队列法

思路

需要一个抽象的类队列数据结构,每轮移动时:1. 把队首pop;2.把下一元素push进队尾;3.获取队列最大值存入数组。

pop实现:每次移动时尝试(说“尝试”是因为可能已经弹出了)弹出队首元素(条件是队列非空且val==que.front)。

push实现:每次移动放入下一元素。放入元素前,从队尾弹出所有小于该元素的值。

getMax实现:返回队首。

复杂度分析

时间复杂度:O(N)。

空间复杂度:O(K)。队列最多有K个元素。

注意点

  1. 这只是单调队列的一种实现(只要保证队列单调递减或递增就是单调队列),实现写法不固定。
  2. deque是c++中stack和queue的底层实现容器,首尾都可以进出。
  3. vector可以push_back。
  4. c++对象声明不需要new(直接声明是生命周期在作用域内,内存从栈上分配;new分配的声明周期在new~delete之间,内存从堆上分配)。new的对象需要指针类型保存,访问时候用->。

代码实现

class Solution {
private:
    class myQue{
        public:
            deque<int> que;
            void pop(int val){
                // 每次移动尝试弹出最左侧的元素
                // 只有val==队首时候弹出(其他元素在push的时候就已经弹出了)
                if(!que.empty() && val == que.front()){
                    que.pop_front();
                }
            }   
            void push(int val){
                // 队尾插入,插入前弹出所有比val小的元素
                while(!que.empty() && val > que.back()){
                    que.pop_back();
                }
                que.push_back(val);
            }     
            int getMax(){
                return que.front();
            }
    };


public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        myQue que;
        vector<int> result;
        // 装填k个元素
        for(int i = 0;i < k;i++){
            que.push(nums[i]);  
        }
        // 获取前k个元素最大值
        result.push_back(que.getMax());
        // 移动size-k次
        for(int i = k;i < nums.size();i++){
            que.pop(nums[i - k]);
            que.push(nums[i]);
            result.push_back(que.getMax());
        }

        return result;
    }
};

前k个高频元素

leetcode:347. 前 K 个高频元素

优先队列法

思路

  1. 遍历数组用map记录元素出现聘书
  2. 遍历map用小堆顶(优先队列)来存储前k个元素。
  3. 倒序遍历结果数组,把优先队列倒序赋值进去。

复杂度分析

时间复杂度:O(N)。N大小的数组、map遍历各1次、k大小的数组遍历1次。

空间复杂度:O(N+K)。

注意点

  1. map等无下标容器用iterator遍历,迭代器加星号*可以取迭代器对应的值。
  2. 优先队列没有front只有top。
  3. 仿函数(Function Object/Functor)是指可以通过函数调用操作符'( )'执行的对象。具体来说是任何定义了'operator()'的类的实例,使得类的对象可以像函数一样调用,因此得名“仿函数”。

代码实现

class Solution {
private:
    class myComparison{
        public:
            bool operator()(const pair<int,int>& lhs,const pair<int,int>& rhs){
                return lhs.second > rhs.second;
            }
    };

public:
    // 用map试试
    // 遍历,用map记录元素出现频数
    // 小堆顶存储k个元素
    // 数组倒序存储结果
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int,int> map;
        for(int i = 0;i < nums.size();i++){
            map[nums[i]]++;
        }

        priority_queue<pair<int,int>,vector<pair<int,int>>,myComparision> minPq;
        for(unordered_map<int,int>::iterator it = map.begin();it != map.end();it++){    
            minPq.push(*it);
            if(minPq.size() > k)
                minPq.pop();
        }

        vector<int> result(k);
        for(int i = k-1;i >= 0;i--){
            result[i] = minPq.top().first;
            minPq.pop();
        }

        return result;
    }
};
posted @ 2024-02-05 23:40  Tazdingo  阅读(1905)  评论(0)    收藏  举报