[代码随想录Day11打卡] 150. 逆波兰表达式求值 239. 滑动窗口最大值 (有点难度) 347.前 K 个高频元素 (有点难度) 总结

devtools/2024/11/16 1:39:04/

150. 逆波兰表达式求值

逆波兰表达式就是后缀表达式。我们日常接触到的1+2 * 3+4是中序表达式,中序表达式往往需要括号来指定执行操作的顺序。后序表达式1 2 + 3 4 + 就是顺序进行不需要括号。
按照输入的数据的类型可以分为数值和操作符。有效的算符为 ‘+’、‘-’、'
’ 和 ‘/’ 。这些都是双目操作符。
比如以1 2 + 3 4 + 为例,遇到数字1 2我们没有操作,遇到操作符+我们需要将前面的数字进行加法操作得到3。遇到数字3 4 我们没有操作,遇到+,我们需要将前两个数字进行加法操作得到7。遇到操作符我们需要将前面获得的两个结果进行乘法操作。
遇到操作符操作的都是离操作符最近的两个元素或者说相邻的两个元素,先入后出使用栈。
两个操作:

  1. 遇到数字就将数字压入栈。
  2. 遇到操作符就连续弹出两个元素,并对其进行操作。先入后出,所以先弹出的元素num1,和后弹出的元素num2,两个的操作应该是num2 op num1。操作得到的结果压入到栈。
  3. 最后将栈中的数值弹出就好。
    下面是C++,JAVA和Python的实现。
class Solution {
public:int evalRPN(vector<string>& tokens) {stack<long long> st;for(int i = 0; i <tokens.size(); i++){//遍历整个字符串if(tokens[i] == "+"||tokens[i]=="-"||tokens[i]=="*"||tokens[i]=="/"){long long num1 = st.top();st.pop();long long num2 = st.top();st.pop();if(tokens[i]=="+") st.push(num2+num1);if(tokens[i]=="-") st.push(num2-num1);if(tokens[i]=="*") st.push(num2*num1);if(tokens[i]=="/") st.push(num2/num1);}else st.push(stoll(tokens[i]));//这个注意是使用stoll来将string类型变成long long 类型}int result = st.top();st.pop();return result;}
};
class Solution {public int evalRPN(String[] tokens) {Deque<Integer> stack = new LinkedList();//JAVA中定义栈的方法for(String s: tokens){if("+".equals(s)){stack.push(stack.pop()+stack.pop());}else if("-".equals(s) ){stack.push(-stack.pop()+stack.pop());}else if("*".equals(s)){stack.push(stack.pop()*stack.pop());}else if("/".equals(s)){int temp1 = stack.pop();int temp2 = stack.pop();stack.push(temp2/temp1);}else{stack.push(Integer.valueOf(s));//注意这个是将string变成int的}}int result = stack.pop();return result;}
}
python">from operator import add, sub, mul
def div(x, y):#使用整数除法的向零取整方式return int(x/y) if x*y >0 else -(abs(x)//abs(y))
class Solution(object):def evalRPN(self, tokens):""":type tokens: List[str]:rtype: int"""stack = []op_map = {'+': add, '-': sub, '*': mul, '/': div}for token in tokens:if token in {'+','-','*','/'}:num1 = stack.pop()num2 = stack.pop()operator = op_map[token]stack.append(operator(num2, num1))else:stack.append(int(token))return stack.pop()

参考文章

  1. https://programmercarl.com/0150.%E9%80%86%E6%B3%A2%E5%85%B0%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%B1%82%E5%80%BC.html

239. 滑动窗口最大值 (有点难度)

关键:定义单调队列。维护最大值的队列。
单调队列的功能:pop() push() getMaxValue() 三个函数好好定义。
定义一个队列,遍历列表,将列表中的值放到队列中。
滑动窗口两个操作:

  1. 旧元素出去pop。每次滑窗移动的时候判断一下要pop的值是不是队列中que.front()的最大值,如果是就pop,不是就不进行pop。
  2. 新元素进来push。如果当前值大于前面的值就将前面的值pop掉,这样队列的que.front()就是最大值。
  3. 得到最大值front。就是队列的que.front()。
    定义完这个单调队列之后,进行操作。每次移动的时候就是pop(nums[i-k]),push(nums[i]),然后front()得到窗口中的最大值保存到结果列表中。
class Solution {
private:class MyQueue{public:deque<int> que;//使用deque实现队列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 getMaxValue(){return que.front();}};
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {MyQueue que;vector<int> result;for(int i = 0; i<k; i++){que.push(nums[i]);//这个就是使用我们定义的单调队列}result.push_back(que.getMaxValue());//第一个窗口的最大值for(int i = k; i< nums.size(); i++){que.pop(nums[i-k]);que.push(nums[i]);result.push_back(que.getMaxValue());}return result;}
};
class MyQueue{Deque<Integer> deque = new LinkedList<>();void poll(int val){if(!deque.isEmpty() && val == deque.peek()){deque.poll();}}void add(int val){//这个是whilewhile(!deque.isEmpty() && val>deque.getLast()){deque.removeLast();}deque.add(val);}int peek(){return deque.peek();}
}
class Solution {public int[] maxSlidingWindow(int[] nums, int k) {if (nums.length == 1) {return nums;}MyQueue myque = new MyQueue();int len = nums.length - k +1;int[] res = new int[len];int count = 0;for(int i = 0; i < k; i++){myque.add(nums[i]);}res[count++] = myque.peek();for(int i =k ; i < nums.length; i++){myque.poll(nums[i-k]);myque.add(nums[i]);res[count++] = myque.peek();}return res;}
}
python">class MyQue:def __init__(self):self.queue = deque()def pop(self, value):if self.queue and value == self.queue[0]:self.queue.popleft()def push(self, value):while self.queue and value> self.queue[-1]:self.queue.pop()self.queue.append(value)def front(self):return self.queue[0]
class Solution(object):def maxSlidingWindow(self, nums, k):""":type nums: List[int]:type k: int:rtype: List[int]"""que = MyQue()result = []for i in range(k):que.push(nums[i])result.append(que.front())for i in range(k, len(nums)):que.pop(nums[i-k])que.push(nums[i])result.append(que.front())return result

参考文章

  1. https://programmercarl.com/0239.%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E6%9C%80%E5%A4%A7%E5%80%BC.html

347.前 K 个高频元素(有点难度)

关键:定义最小堆。维护K个值的顺序。
需要保存元素及其对应的频率,所以使用map。因为只需要找到前k个高频元素所以我们只需要维护频率最高的K个元素的顺序就好。
小顶堆,pop的时候堆顶弹出的是堆中最小的元素。我们就使用小顶堆遍历列表,然后保持小顶堆的长度为K就好。
对我来说不同语言的小顶堆定义是很困难的。
前面获得每个元素及其对应的频率就是哈希表。定义好了小顶堆就只需要push,当小顶堆的size大于K pop就可以。我看JAVA中也有使用大顶堆的(没仔细看,可能把所有元素和频率都放到大顶堆中,然后弹出K个元素)。

class Solution {
public://小顶堆class mycomparison{public:bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs){return lhs.second > rhs.second;}//函数重载};//是不是所有class最后都要有分号vector<int> topKFrequent(vector<int>& nums, int k) {//要统计元素出现的频率unordered_map<int,int> map;//map<nums[i],对应出现的次数>for(int i = 0; i < nums.size(); i++){map[nums[i]]++;//次数加一}//遍历一遍得到相应的频率之后,对频率进行排序//定义一个小顶堆,大小为kpriority_queue<pair<int, int>,vector<pair<int, int>>, mycomparison> pri_que;//用固定大小为k的小顶堆,扫描是哟有频率的数值,只维持前k个for(unordered_map<int, int>::iterator it = map.begin(); it !=map.end(); it++){pri_que.push(*it);if(pri_que.size() > k){//堆大小大于k就弹出,维持堆的大小为kpri_que.pop();}}//找出k个高频元素,小顶堆先弹出最小的,倒叙输出到数组vector<int> result(k);for(int i = k-1; i>=0;i--){result[i] = pri_que.top().first;pri_que.pop();}return result;}
};
class Solution {public int[] topKFrequent(int[] nums, int k) {Map<Integer, Integer> map = new HashMap<>();//key为数组元素值,val为对应出现的次数for (int num : nums){//统计每个元素出现的次数map.put(num, map.getOrDefault(num, 0)+1);//如果对应的值存在就是使用索引得到的值,否则就是初始化为0}PriorityQueue<int[]> pq = new PriorityQueue<>((pair1, pair2)->pair1[1]-pair2[1]);//如果pair1[1]-pair2[1]小于0则pair1放到pair2的前面否则就是pair1放到pair2的后面for(Map.Entry<Integer, Integer> entry: map.entrySet()){//小顶堆只需要维护k个元素的顺序if(pq.size() < k){//小顶堆元素个数小于k个是直接加pq.add(new int[]{entry.getKey(), entry.getValue()});//这个就是存储一个列表,然后小顶堆进行排序的时候只是看第二个元素}else {if (entry.getValue() > pq.peek()[1]){//当前出现的频率大于小顶堆的顶点就弹出然后pushpq.poll();pq.add(new int[]{ entry.getKey(), entry.getValue()});}}}int[] ans = new int[k];//按照顺序输出前k个高频词for (int i = k -1 ; i >= 0; i--){//一次弹出小顶堆,但是逆序添加到result中ans[i] = pq.poll()[0];}return ans;}
}
python">import heapq
class Solution(object):def topKFrequent(self, nums, k):""":type nums: List[int]:type k: int:rtype: List[int]"""map_ = {}#记录元素及对应的出现的次数for i in range(len(nums)):map_[nums[i]] = map_.get(nums[i], 0)+1#对频率进行排序,看看python怎么定义小顶堆的pri_que = []#用固定大小的小顶堆遍历所有频率的数值for key, freq in map_.items():#遍历整个mapheapq.heappush(pri_que,(freq, key))#这个是做什么的呢,这个是不是实现小顶堆的if len(pri_que) > k:#维护小顶堆内的数据只有k个heapq.heappop(pri_que)#找出前K个元素。倒序输出result = [0]*kfor i in range(k-1, -1, -1):result[i] = heapq.heappop(pri_que)[1]return result

参考文章

  1. https://programmercarl.com/0347.%E5%89%8DK%E4%B8%AA%E9%AB%98%E9%A2%91%E5%85%83%E7%B4%A0.html

总结

感觉还得二刷。

参考文章

  1. https://programmercarl.com/%E6%A0%88%E4%B8%8E%E9%98%9F%E5%88%97%E6%80%BB%E7%BB%93.html

http://www.ppmy.cn/devtools/134308.html

相关文章

反向代理模块

1 概念 1.1 反向代理概念 反向代理是指以代理服务器来接收客户端的请求&#xff0c;然后将请求转发给内部网络上的服务器&#xff0c;将从服务器上得到的结果返回给客户端&#xff0c;此时代理服务器对外表现为一个反向代理服务器。 对于客户端来说&#xff0c;反向代理就相当于…

基于微信小程序的养老院管理系统的设计与实现,LW+源码+讲解

摘 要 伴随着互联网发展&#xff0c;其基础理论与技术都已完善&#xff0c;并积极参与到整个社会各个方面。它让信息可以通过媒体传播&#xff0c;相互配合信息管理专用工具能够为大家提供优质的服务。对于传统信息管理错乱、差错率高、信息安全系数差、工作强度大、耗时费力…

Rocky9/Ubuntu使用pip安装python的库mysqlclient失败解决方式

# Rocky9 直接使用pip安装mysqlclient会出现缺少依赖&#xff0c;需要先安装mysql-devel相关依赖。由于rocky9用MariaDB替代了MySQL&#xff0c;所以我们可以通过安装mariadb-devel来安装所需要的依赖。 如果Rocky9已经开启了powertool repo可以直接使用下面命令安装 dnf in…

Android 老项目适配 Compose 混合开发

app 模块下的 build.gradle 添加: buildFeatures {compose = true} composeOptions {kotlinCompilerExtensionVersion = "1.4.3"} 引用入 compose 组件库: val compose_version = "1.6.2" implementation("androidx.compose.ui:ui:$compose_versi…

Redis 典型应用 - 缓存(cache)

一、什么是缓存 缓存(cache)是计算机中的⼀个经典的概念.在很多场景中都会涉及到. 核⼼思路就是把⼀些常⽤的数据放到触⼿可及(访问速度更快)的地⽅,⽅便随时读取. 这⾥所说的"触⼿可及"是个相对的概念. 对于硬件的访问速度来说,通常情况下: CPU寄存器>内存>…

时序预测 | Python基于CNN-transformer时间序列预测

时序预测 | Python基于CNN-transformer时间序列预测 目录 时序预测 | Python基于CNN-transformer时间序列预测预测效果基本介绍参考资料 预测效果 基本介绍 时序预测 | Python基于CNN-transformer时间序列预测 Cnn-transformer-自适应稀疏自注意力ASSA-对比归一化contranorm预…

docker打包nginx版wordpress

官方打包的wordpress的docker版是基于apache&#xff0c;在低配的机器上容易挂掉。所以考虑nginx Dockerfile # 更改基础镜像为PHP 8.x FPM Alpine FROM php:8.2-fpm-alpine# 更新并安装PHP依赖&#xff0c;注意检查扩展与PHP 8.x的兼容性 # 这里不用php8.3 因为安装imagick有…

Mysql个人八股总结

1.一条 SQL 查询语句是如何执行的 第一步&#xff1a;连接器 连接数据库&#xff1a;当用户发起SQL查询时&#xff0c;连接器负责与数据库建立连接&#xff0c;验证用户身份并准备执行查询。 第二步&#xff1a;查询缓存 检查查询缓存&#xff1a;在执行查询之前&#xff0…