150. 逆波兰表达式求值
力扣题目链接(opens new window)
根据 逆波兰表示法,求表达式的值。
有效的运算符包括 + , - , * , / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
说明:
整数除法只保留整数部分。 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:
- 输入: ["2", "1", "+", "3", " * "]
- 输出: 9
- 解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
逆波兰表达式:是一种后缀表达式,所谓后缀就是指运算符写在后面。
平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。
该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。
逆波兰表达式主要有以下两个优点:
-
去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
-
适合用栈操作运算:遇到数字则入栈;遇到运算符则取出栈顶两个数字进行计算,并将结果压入栈中。
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:
op_map = {'+': add, '-': sub, '*': mul, '/': div}
def evalRPN(self, tokens: List[str]) -> int:
# tokens = list(tokens)
stack = []
for i in tokens:
if i not in {'+', '-', '*', '/'}:
stack.append(int(i))
else:
op2 = stack.pop()
op1 = stack.pop() # 这里是难点,"4","13","5","/","+"当到/的时候,第一个pop取出的是5,第二个pop取出的是13
stack.append(self.op_map[i](op1, op2))
return stack.pop()
239. 滑动窗口最大值
力扣题目链接(opens new window)
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
from collections import deque
class MyQUeue:
def __init__(self):
self.queue = deque()
def push(self, value):
while self.queue and value > self.queue[-1]:
self.queue.pop()
self.queue.append(value)
def pop(self, value):
if self.queue and value == self.queue[0]:
self.queue.popleft() # 把最大的元素pop掉,开始添加新元素了
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):
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)push:如果push的元素比前元素大,直接把前面元素删掉,
2)pop:当要pop的值等于que的第一个(最大值)的时候说明这个值要真的被pop掉了,和他本身大小无关,只是滑窗滑到下一个了。
3)front:对于queue序列来说,第一个值queue[0],一直是最大值
347.前 K 个高频元素
力扣题目链接(opens new window)
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:
- 输入: nums = [1,1,1,2,2,3], k = 2
- 输出: [1,2]
示例 2:
- 输入: nums = [1], k = 1
- 输出: [1]
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
map_ = {}
for i in range(len(nums)):
map_[nums[i]] = map_.get(nums[i], 0)+1
pri_que = [] # 小顶堆
for key, freq in map_.items():
heapq.heappush(pri_que, (freq, key))
if len(pri_que) > k:
heapq.heappop(pri_que)
result = [0] * k
for i in range(k-1, -1, -1):
result[i] = heapq.heappop(pri_que)[1]
return result
-
for key, freq in map_.items():
- 这个循环遍历字典
map_
的所有键值对。 key
是字典中的键(通常表示某个元素)。freq
是字典中对应键的值(表示该元素的频率)。
- 这个循环遍历字典
-
heapq.heappush(pri_que, (freq, key))
- 使用
heapq.heappush
函数将元素(freq, key)
推入优先队列pri_que
中。 - 这里使用的是一个小顶堆(默认情况下
heapq
实现的是小顶堆),堆中的每个元素是一个元组(freq, key)
,其中freq
是元素的频率,key
是元素的值。 - 小顶堆的特性是堆顶元素总是最小的,因此当我们插入
(freq, key)
时,堆会自动调整顺序,使频率最小的元素保持在堆顶。
- 使用
-
if len(pri_que) > k:
- 检查堆
pri_que
的大小是否超过了k
。 - 如果堆中元素数量超过了
k
,说明堆中已经有超过k
个元素。
- 检查堆
-
heapq.heappop(pri_que)
- 如果堆的大小超过了
k
,使用heapq.heappop
将堆顶元素弹出。 - 由于这是一个小顶堆,堆顶的元素是堆中频率最小的元素,所以弹出操作会移除当前频率最小的那个元素。
- 通过这种方式,堆的大小始终保持在
k
,并且堆中只保留频率最高的k
个元素。
- 如果堆的大小超过了