Day13 栈与队列 LeetCode 239 347

171 阅读1分钟

239. 滑动窗口最大值

hard

心得

  • 想的暴力遍历,显然不是最优

题解

  • 时间复杂度O(N),空间O(N),虽然push中有while,但是每个元素操作数最多2次
  • 用单调队列解决,而不是优先队列,因为大顶堆每次只能弹出最大值,而窗口移动,无法保持动态更新
  • 队列始终保持单调性(单减),结果即为队首元素
  • 注意队列的非空判断,默认的duque结构支持首尾操作
  • 切记单调队列不是对其中元素进行排序,这样的话跟优先队列没有区别
class MyQueQue {
public:
    deque<int> que;  // 默认队列使用deque结构,双端可操作
    // 移除时,如果不是队首元素则保留,即队列保留的是单调队列,最值,可能不在原来的第一个位置
    void pop(int value){
        if (!que.empty() && que.front() == value) {
            que.pop_front();
        }
    }
    // 添加元素时,如果当前值大于之前的则之前的全部出队,仅维护该最大值
    void push(int value){
        while (!que.empty() && value > que.back()) {
            que.pop_back();
        }
        que.push_back(value);
    }
    // 队首即为最大值
    int front(){
        return que.front();
    }

};

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        MyQueQue que;
        vector<int> result;
        for (int i = 0; i < k; i++) {
            que.push(nums[i]);
        }
        result.push_back(que.front());
        for (int i = k; i < nums.size(); i++) {
            que.pop(nums[i-k]);
            que.push(nums[i]);
            result.push_back(que.front());
        }
        return result;
    }

};

347. 前 K 个高频元素

mid

心得

  • 考虑map保存次数,然后value排序,但是感觉两步骤麻烦
  • 题目给的暗示复杂度NLogN考虑,先遍历N,然后快排排序不超过NlogN,但是排序其实不需要所有元素都参与,主要k个即可,考虑此二叉树调整正好logK
  • 多思考多尝试

题解

  • 时空复杂度nlongk,n
  • top k问题,大小堆,堆是一颗完全二叉树,树中每个结点的值都不大于或者小于其左右孩子的值,如果大顶堆,则父节点大于左右孩子,反之小顶堆
  • C++中优先级队列(priority_queue)即为大小堆的实现,由于其对外接口只是从对头取元素,队尾添加元素,看起来很像一个队列,默认大顶堆,小顶堆需要自己写comparison
  • 本题用的小顶堆,如果用大顶堆,每次堆顶是最大元素,无法前k个,所以用的小顶堆,维护size k,超过k每次移除最小即可
class Solution {
public:
    // 小顶堆
    class mycomparison {
    public:
        bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {
            return lhs.second > rhs.second;
        }
    };

    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> map;
        for (int i = 0; i < nums.size(); i++) {
            map[nums[i]]++;
        }
        // 频率排序,定义小顶堆,大小k,写法看不懂? 
        priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que;
        // 固定大小k的小顶堆,扫描所有频率的数值
        for (unordered_map<int, int>::iterator it = map.begin(); it != map.end(); it++) {
            pri_que.push(*it);
            if (pri_que.size() > k) { // 始终保持大小k,移除最小的,这样剩下的就是最大的
                pri_que.pop();
            }
        }
        // 优先队列的值目前只能弹出最小,所以需要逆序
        vector<int> result(k);
        for (int i = k-1; i >= 0; i--) {
            result[i] = pri_que.top().first;
            pri_que.pop();
        }
        return result;
    }
};