150. 逆波兰表达式求值
状态:有思路但不太确定,看视频讲解后,自己写代码
思路:使用栈:遇到数字就入栈,遇到运算符,则取出栈顶前两个元素进行运算,并将结果放回栈中,以此类推
总结:
1. 思路比较明晰,但对比示例代码后,觉得自己写代码方面的优化能力有待提高:使用python的operator模块。
摘自笔记:Python 什么情况下应该使用Python内置的 ‘operator’ 模块|极客笔记
在什么情况下使用Python内置的 ‘operator’ 模块?
- 需要在运行时动态执行不同的运算符操作。
- 需要对容器对象(例如列表、元组和字典)中的元素进行比较或排序操作。
- 需要对自定义的对象进行比较操作,以便能够正确排序或比较它们。
通过使用 ‘operator’ 模块,我们可以避免编写大量的条件语句来处理不同的运算符操作,从而使代码更加简洁、清晰,并提高运行效率。
# 示例代码(来自代码随想录) 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): op_map = {'+': add, '-': sub, '*': mul, '/': div} 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()
2. 题目要求“整数除法只保留整数部分”,而python中的 '/' 不是整除,‘//’是floor除法(向下舍入),因此要对除法做特殊处理(可以参考上述的示例代码),我自己的代码中用的是强制int转换。
3. 要注意入栈时,数据类型从str --> int的转换
4. 时间复杂度: O(n),空间复杂度: O(n)
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
s = []
for i in tokens:
if i == '+' or i =='-' or i =='*' or i == '/':
num1 = s.pop()
num2 = s.pop()
if i == '+':
s.append(num2 + num1)
if i =='-':
s.append(num2 - num1)
if i == '*':
s.append(num2 * num1)
if i == '/':
s.append(int(num2 / num1))
else:
s.append(int(i))
return s[-1]
239. 滑动窗口最大值
状态:想到要用队列,但是对于具体用哪种队列,怎么写代码没有思路,也没有意识到找最大值的难度。看完视频讲解和示例代码后,自己学着写代码
思路:设计一个单调队列,具有pop, push和getMaxvalue功能。该队列只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。
摘自代码随想录:
设计单调队列的时候,pop,和push操作要保持如下规则:
- pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
- push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止
保持如上规则,每次窗口移动的时候,只要问que.front()就可以返回当前窗口的最大值。
总结:
1. 学习了单调队列的知识,以及为什么不能用优先级队列的原因
2. 在push(value)中,直接将小于value的入口元素全部弹出,队列里保证只留有可能成为窗口最大值的元素
3. 在主函数遍历数组nums[k, len(nums)]时的逻辑是:先pop第 j - k个元素(即出口元素),再push元素,之后记录窗口最大值
4. 时间复杂度: O(n), 空间复杂度: O(k)
from collections import deque
class MyQue:
def __init__(self):
self.queue = deque()
def pop(self, val: int):
if self.queue and val == self.queue[0]:
self.queue.popleft()
def push(self, val: int):
while self.queue and val > self.queue[-1]:
self.queue.pop()
self.queue.append(val)
def getMaxValue(self):
return self.queue[0]
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
que = MyQue()
result = []
for i in range(k):
que.push(nums[i])
result.append(que.getMaxValue())
for j in range(k, len(nums)):
que.pop(nums[j-k])
que.push(nums[j])
result.append(que.getMaxValue())
return result
347.前 K 个高频元素
状态:想到用字典记录数字出现频率,但不知道如何让用队列解决取前k个高频元素的问题。看完视频讲解和示例代码后,自己学着写代码
思路:分3步:1)用字典记录频率;2)使用小顶堆进行排序;3)返回前k个高频元素
总结:
1. 初步了解优先级队列(小顶堆)的知识:小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素
2. python小顶堆可用heapq模块实现,heappush和heappop分别用来存入和弹出元素;注意存入堆时要按照(freq, key)存入,即按照freq进行排序
3. leetcode上不要求从大到小返回前k个元素,但返回的是key, 即heapq.heappop(que)[1]
4. 时间复杂度: O(nlogk), 空间复杂度: O(n)
import heapq
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
dict_ = {}
for i in range(len(nums)):
dict_[nums[i]] = dict_.get(nums[i], 0) + 1
que = []
for key, freq in dict_.items():
heapq.heappush(que, (freq, key)) # 按照频率大小排序
if len(que) > k:
heapq.heappop(que)
# 任意顺序返回答案
res = []
for i in range(k):
res.append(heapq.heappop(que)[1])
return res