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

day12休息

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

150. 逆波兰表达式求值

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

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

解题思路

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

代码

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<long> sta;
        for(int i=0; i<tokens.size(); i++){
            long num, num1, num2;
            if(tokens[i]=="+" || tokens[i]=="-" || tokens[i]=="*" || tokens[i]=="/"){
                num1 = sta.top();
                sta.pop();
                num2 = sta.top();
                sta.pop();
                if(tokens[i]=="+") sta.push(num1+num2);
                if(tokens[i]=="-") sta.push(num2-num1);
                if(tokens[i]=="*") sta.push(num1*num2);
                if(tokens[i]=="/") sta.push(num2/num1);
            }
            else sta.push(stoi(tokens[i]));
        }
        int result = sta.top();
        return result;
    }
};

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<int> topKFrequent(vector<int>& nums, int k) {
        class mycomparison {
        public:
            bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {
                return lhs.second > rhs.second; // 左大于右,小顶堆,右大于左,大顶堆
            }
        };
        
        // 用map统计元素出现频率
        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){
                pri_que.pop();
            }
        }

        // 找出k个高频元素,因为小顶堆先弹出小的值,所以倒叙输出到数组
        vector<int> 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(完全二叉树)

堆是一棵完全二叉树

栈的总结

代码随想录

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值