逆波兰表达式求值
给你一个字符串数组
tokens
,表示一个根据 逆波兰表示法 表示的算术表达式。请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为
'+'
、'-'
、'*'
和'/'
。- 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
示例 1:
输入:tokens = ["2","1","+","3","*"] 输出:9 解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9示例 2:
输入:tokens = ["4","13","5","/","+"] 输出:6 解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
什么是逆波兰表达式? 就是后缀表达式,相比于我们正常的中缀表达,它不需要括号就可以正确运算。
如何对逆波兰表达式求值? 利用栈,原理就是将数字按顺序放入栈中,碰到运算符就在栈中取出两个数字进行计算,再将计算结果放入栈中,重复上述步骤。
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
stack = []
for item in tokens:
if item not in {"+", "-", "*", "/"}:
stack.append(item)
else:
first_num, second_num = stack.pop(), stack.pop()
stack.append(
int(eval(f'{second_num} {item} {first_num}')) # 第一个出来的在运算符后面
)
return int(stack.pop())
- 使用集合{},而不用 [ ]: 更高效,对于集合来说,使用
item not in {"+", "-", "*", "/"}
进行成员检查时,底层会利用哈希表(hash table)的特性来进行快速的成员查找,而不需要像列表一样逐一匹配。 - int(eval(f'{second_num} {item} {first_num}')) :
-
f'{second_num} {item} {first_num}'
: 这部分代码使用了 f-string 格式化字符串,将两个变量second_num
和first_num
以及操作符item
组合成一个字符串表达式。例如,如果second_num
的值为 5,first_num
的值为 3,item
的值为 '+',那么这个表达式就会被格式化为'5 + 3'
。 -
eval()
:eval()
函数用于执行一个字符串表达式,并返回表达式的值。在这里,eval()
函数会计算字符串表达式'5 + 3'
的值,即 8。
使用eval相对较慢,可以选择一个更高效的方法
from operator import add, sub, mul
class Solution:
op_map = {'+': add, '-': sub, '*': mul, '/': lambda x, y: int(x / y)}
def evalRPN(self, tokens: List[str]) -> int:
stack = []
for token in tokens:
if token not in {'+', '-', '*', '/'}:
stack.append(int(token))
else:
op2 = stack.pop()
op1 = stack.pop()
stack.append(self.op_map[token](op1, op2)) # 第一个出来的在运算符后面
return stack.pop()
滑动窗口最大值
给你一个整数数组
nums
,有一个大小为k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的k
个数字。滑动窗口每次只向右移动一位。返回 滑动窗口中的最大值 。
这是使用单调队列的经典题目。
此时我们需要一个队列,这个队列呢,放进去窗口里的元素,然后随着窗口的移动,队列也一进一出,每次移动之后,队列告诉我们里面的最大值是什么。
- 因为我们本题只需要求最大值,本质上我们只需要维护最大值元素就可以了→而我们的最大值又需要记录下来,因此我们需要实现一个单调队列
- 如何实现单调队列?→因为只需要维护最大值,所以当新元素从队尾加进来时,只需要将前面所有比它小的元素清除,这样就能保证队列是单调的
- 如何更新滑动窗口?→对于需要删除的元素,看一下是否跟队尾的元素相同,相同的话就删除掉它;对于加入的元素按照维护最大值的规则加入即可
from collections import deque
class MyQueue: #单调队列(从大到小
def __init__(self):
self.queue = deque() #这里需要使用deque实现单调队列,直接使用list会超时
#每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。
#同时pop之前判断队列当前是否为空。
def pop(self, value):
if self.queue and value == self.queue[0]:
self.queue.popleft()#list.pop()时间复杂度为O(n),这里需要使用collections.deque()
#如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。
#这样就保持了队列里的数值是单调从大到小的了。
def push(self, value):
while self.queue and value > self.queue[-1]:
self.queue.pop()
self.queue.append(value)
#查询当前队列里的最大值 直接返回队列前端也就是front就可以了。
def front(self):
return self.queue[0]
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
que = MyQueue()
result = []
for i in range(k): #先将前k的元素放进队列
que.push(nums[i])
result.append(que.front()) #result 记录前k的元素的最大值
for i in range(k, len(nums)):
que.pop(nums[i - k]) #滑动窗口移除最前面元素
que.push(nums[i]) #滑动窗口前加入最后面的元素
result.append(que.front()) #记录对应的最大值
return result