打卡代码随想录算法训练营第11天: 150. 逆波兰表达式求值 239. 滑动窗口最大值 347.前 K 个高频元素

代码随想录

文中含LLM回答内容

150. 逆波兰表达式求值

力扣题目链接

思路K:先理解逆波兰表达式是啥,是把运算符放在了两个要运算的数字的后边,又叫后缀表达式。

遇见数字就入栈,遇见算符就计算栈里前两个数字,算完再存回去

class Solution {
public:
    int evalRPN(vector& tokens) {
        stack poland;
        for(int i=0;i

基本功:要分清string与char。

239. 滑动窗口最大值

力扣题目链接

使用deque来实现单调队列,deque(双端队列)是一种两端都可以进行高效操作的数据结构,名字来源于 double-ended queue。它是 C++ 标准模板库(STL)中的一个容器,允许在前端(front)和后端(back)高效地插入或删除元素

单调队列的目的是在一个滑动窗口中 高效地维护最大值,而不是遍历整个窗口来查找最大值

利用这个定长的单调队列去遍历数组,即模拟滑动窗口

  • 高效获取窗口最大值:

    • 使用 que.front() 可以在 O(1)O(1)O(1) 时间内获得当前窗口的最大值。
      标准库中的 queuedeque 没有为这种操作提供内置的功能,直接用它们只能通过遍历来找到最大值,时间复杂度为 O(k)O(k)O(k)。
  • 在窗口滑动时高效更新队列:

    • pushpop 方法都维护了队列的单调性(从大到小),从而在 O(1)O(1)O(1) 的均摊时间内完成更新。

个人认为难点在于理解自定义的pop()函数

class Solution {
private:
    class Myqueue{
    public:
        deque que;
        void pop(int val){
            if(!que.empty() && val==que.front()){   //无名小卒早就被清理了,判断有能力的还在不在,要在的话就到时间该清了(借鉴力扣评论)
                que.pop_front();
            }
        }
        void push(int val){
            while(!que.empty() && val > que.back()){
                que.pop_back();
            }
            que.push_back(val);
        }
        int front(){
            return que.front();
        }
    
    };    
public:
    vector maxSlidingWindow(vector& nums, int k) {
        Myqueue que;
        vector result;
        for (int i=0;i

347.前 K 个高频元素

力扣题目链接

很吃语法和基础知识的一道题

思路:将数字和出现次数以(key)和(value)存进一个map,用固定大小为k的顶堆,扫描一遍map,自定义小顶堆的优先级队列,利用小顶堆最小值一直在上面的性质,不断pop出出现频率低的,留出现频率高的。

//小顶堆虽然不能保证堆内字符完全有序排列,因为是完全二叉树,但能保证头部(或者说是top部、根部是最大的)
class Solution {
public:
    struct compare{
        bool operator()(const pair& pa,const pair& ch){    //operator在这里是仿函数用法
            return pa.second > ch.second;         //这里是>
        }
    };
    
    vector topKFrequent(vector& nums, int k) { 
        std::unordered_map map;
        for(int i=0;i,vector>,compare> que;
        for(auto it=map.begin();it!=map.end();it++){
            que.push(*it);       //*it 是解引用操作符,用于访问迭代器 it 所指向的具体元素
            if(que.size()>k){
                que.pop();
            }
        }
        vector result(k);      //初始化大小为k是必要的,不然会报错 
        for(int j=k-1;j>=0;j--){    //下标是从0到k-1
            result[j]=que.top().first;
            que.pop();
        }
        return result;
    }
};

时间复杂度是O(nlogk)

  • 时间复杂度

    • 构建频率表的时间复杂度是 O(n),其中 n 是 nums 数组的大小。
    • 上浮操作时间复杂度为堆的深度,由于堆是完全二叉树,它的深度度是 log k数量级,k为堆中元素数量,插入元素到堆中的时间复杂度是 O(log k),总共插入了 n 个元素,最终堆中保存了 k 个元素,因此总体复杂度为 O(n log k)。
    • 结果的填充时间复杂度是 O(k),这部分是常数级别的,所以总体复杂度是 O(n log k)。
  • 空间复杂度

    • unordered_map 需要 O(n) 的空间来存储频率表。
    • priority_queue 需要 O(k) 的空间来存储堆中的元素。
    • result 需要 O(k) 的空间来存储最终的 K 个元素。

总的来说,时间复杂度是 O(n log k),空间复杂度是 O(n + k)。

你可能感兴趣的:(leetcode)