day 13(1003) 第五章 栈与队列

day12休息

day13学习内容:150. 逆波兰表达式求值、239. 滑动窗口最大值、347.前 K 个高频元素、栈的总结

150. 逆波兰表达式求值

后缀表达式(逆波兰表达式)

先把式子转为二叉树,下面的第一个式子为后缀表达式,第二个式子是常用的中序表达式,但缺点是需要加括号,所以计算机内部一般用后缀表达式(后续遍历 顺序是左右中)

day 13(1003) 第五章 栈与队列_第1张图片

解题思路

遇见数字就加入到栈里,遇到操作符就从栈里取出两个元素进行运算,结束后再放回栈里

代码

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

int提交时会溢出,所以改为long

小结

栈非常擅长这种用相邻字符,并进行消除的操作,前面括号匹配也是这样

239. 滑动窗口最大值(单调队列)

题目分析

用队列解决,把队列当作窗口,每移动一次都去掉一个元素,添加一个元素,然后找最大

普通队列不好求最大值

如果使用优先级队列,放到大顶堆里(元素递减)或小顶堆,可能要删除的元素在中间,从而不好删除

所以要使用单调队列,关键在于如何push和pop元素,由自己定义

思路-自定义单调队列

设计单调队列的时候,pop,和push操作要保持如下规则:

  1. pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
  2. push(value):如果push的元素value大于入口元素的数值,那么就将队列出口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止

保持如上规则,每次窗口移动的时候,只要问que.front()就可以返回当前窗口的最大值。

代码

先跳过本题,思路可以理解,感觉串起来写代码还有些难度

小节

单调队列不是一成不变的,不同场景有不同写法,只是要保证队列里单调递减或递增的原则

347.前 K 个高频元素(优先级队列)

题目分析

主要工作分三部分:1-先统计出频率;2-对频率进行排序;3-取出前k个高频对应的值

可以用map,用Key存值,value存其出现的次数,然后对value进行排序,即使是快排(二分法)时间复杂度也是n*logn

但没有必要都进行排序,可以用大顶堆(根节点比子节点大)和小顶堆(根节点比子节点小)数据结构来解决前k个高频或低频元素

如果用大顶堆,会在pop时把最大元素弹出去了,所以大顶堆是前k个低频元素,所以要用小顶堆,弹出的是堆顶的小元素

堆是二叉树,每次排序是维护k个元素,时间复杂度是n*logk

C++中有现成的优先级队列

思路

值和次数放到map中

定义优先级队列

用优先级队列把map遍历完

把优先级队列中元素倒着放入(频率高的在前面,频率低的在后面)

代码

对于大顶堆定义和遍历map不太熟悉,

顶堆可以等二叉树学完再回头看

class Solution {
public:
    vector topKFrequent(vector& nums, int k) {
        class mycomparison {
        public:
            bool operator()(const pair& lhs, const pair& rhs) {
                return lhs.second > rhs.second; // 左大于右,小顶堆,右大于左,大顶堆
            }
        };
        
        // 用map统计元素出现频率
        unordered_map map;
        for(int i=0; i, vector>, mycomparison> pri_que;
        // 用固定大小为k的小顶堆,扫面所有频率的数值
        // 主要搞不太懂小顶堆的机制,学完二叉树再返回来看
        for(unordered_map::iterator it = map.begin(); it != map.end(); it++){
            pri_que.push(*it);
            if(pri_que.size() > k){
                pri_que.pop();
            }
        }

        // 找出k个高频元素,因为小顶堆先弹出小的值,所以倒叙输出到数组
        vector result(k);
        for(int i=k-1; i>=0; i--){
            result[i] = pri_que.top().first;  // 地址的话是->first,但存的是map(*it),则直接.first
            pri_que.pop();
        }
        return result;
    }
};

照着写了一遍,基本理解整体思路,主要对大顶堆定义不熟悉,但感觉是背

小结

优先级队列内部元素是自动依照元素的权值排列,在缺省情况下priority_queue利用max-heap(大顶堆)完成对元素的排序,这个大顶堆是以vector为表现形式的complete binary tree(完全二叉树)

堆是一棵完全二叉树

栈的总结

代码随想录

队列的典型应用——单调队列、优先级队列两个比较难,第一遍不太熟悉,没有独立完成代码

你可能感兴趣的:(代码随想录算法训练营,算法)