代码随想录day12-栈与队列(2)

写在前面

今天的题目涉及到两个知识点,单调队列以及优先级队列,需要好好学习一下二者的性质。

1、LeetCode 150 逆波兰表达式求值

题目分析:
本题读题刚开始还有一点儿费劲,理解不了题目的意思。其实就是一个使用栈的经典题目。我们进来是数字就入栈,然后遇到符号就把栈顶以及栈顶下面一个数做运算即可,将这两个值弹出,然后将运算结果重新压入栈。注意运算的左右。应该是栈顶是右边的,栈顶下一个是左边的。

题目解答:

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> stk;
        for (auto& ch : tokens) {
            // ch为数字
            if (ch != "+" && ch != "-" && ch != "*" && ch != "/") {
                // 将string转为int的方法
                stk.push(stoll(ch));
            }
            else {
                int right = stk.top();  // 右边的值
                stk.pop();
                int left = stk.top();  // 左边的值
                stk.pop();
                if (ch == "+") stk.push(left + right);
                else if (ch == "-") stk.push(left - right);
                else if (ch == "*") stk.push(left * right);
                else stk.push(left / right);
            }
        }
        return stk.top();  // 最终留在栈的值就是最终的结果
    }
};

2、LeetCode 239 滑动窗口的最大值

题目分析:
本题就是典型的单调队列的题目。本题需要自己定义一个单调队列来实现相应的功能。

题目解答:

class Solution {
public:
    // 自己维护一个deque,使其达到单调队列的功能
    class MyDeque {
    public:
        deque<int> que;
        
        // 主要就实现这三个函数
        void pop(const int& val) {
        // 每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。
        // 同时pop之前判断队列当前是否为空。
            // 滑动窗口后移一位,这里传的就是移出去的,但是有可能那些小的值根本就没进来,所以这里要进行一个判断
            if (!que.empty() && que.front() == val) {
                que.pop_front();
            }
        }

        void push(const int& val) {
            while (!que.empty() && val > que.back()) {
                que.pop_back();
            }
            que.push_back(val);  // 再添加进去
        }

         int front() {
             return que.front();
        }
    };

    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        MyDeque myDeque;
        vector<int> ans;
        // 将前k个元素压入单调队列,注意,队列里可能没有k个,因为小的删除了
        for (int i = 0; i < k; i++) {
            myDeque.push(nums[i]);
        }
        ans.emplace_back(myDeque.front());  // 对头就是最大值
        for (int i = k; i < nums.size(); i++) {
            myDeque.push(nums[i]);  // 看缘分,不一定压得进去
            myDeque.pop(nums[i - k]);  // 可能已经出去了,这里是滑动窗口移动一位
            ans.emplace_back(myDeque.front());  // 存这个窗口的最大值
        }
        return ans;
    }
};

本题中实现单调队列只是一种场景,注意不是所有的单调队列都是这么写的。单调队列也算是一个比较重要的知识点了。
本题如果使用暴力法会直接超时。

3、LeetCode 347 前k个高频元素

题目分析:
本题是典型的可以使用优先级队列的题目。题目要求我们统计前k个最高频率的元素,对于统计元素出现的次数,我们可以使用哈希表,但是得到了哈希表之后,我们怎么根据哈希表的值的个数进行排序呢?我们可以将其转换为vector,然后利用重写vector的元素的比较运算符,就可以了。这样做是可行的,有一个容器适配器优先级队列priority_queue就可以很好解决这个问题。
题目解答:

class Solution {
public:
    // 谓词,自定义排序规则
    class myCompare {
    public:
        bool operator()(const pair<int, int>& pair1, const pair<int, int>& pair2) {
            return pair1.second > pair2.second;  // 按照倒序排列
        }
    };


    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> umap;
        vector<int> ans;
        // 统计元素出现的次数用哈希表
        for (auto& num : nums) {
            umap[num]++;
        }

        // 用底层容器为vector>实现优先级队列
        priority_queue<pair<int, int>, vector<pair<int, int>>, myCompare> pri_que;

        // 用固定大小为k的小顶堆,扫面所有频率的数值
        for (auto it = umap.begin(); it != umap.end(); it++) {
            pri_que.push(*it);  // *it是个pair
            if (pri_que.size() > k) pri_que.pop();  // 如果堆的大小大于了k,则队列弹出,保证堆的大小一直为k
        }
        
        // 这里的top是指优先级最低的,pop也是弹出优先级最低的
        for (int i = k - 1; i >= 0; i--) {
            ans.emplace_back(pri_que.top().first);
            pri_que.pop();
        }
        return ans;
    }
};

注意这里的优先级队列priority的原型是:

template <typename T,
        typename Container=std::vector<T>,
        typename Compare=std::less<T> >
class priority_queue{
    //......
}

其中第二个参数是优先级队列具体的实现容器,我们这里使用了vector,第三个参数是优先级队列的排序方法,一般是谓词

你可能感兴趣的:(代码随想录刷题,leetcode,算法,数据结构)