五、栈与队列(2)
- [150. 逆波兰表达式求值 ](https://leetcode.cn/problems/evaluate-reverse-polish-notation/description/)
- [239. 滑动窗口最大值 ](https://leetcode.cn/problems/sliding-window-maximum/description/)
- [347.前 K 个高频元素 ](https://leetcode.cn/problems/top-k-frequent-elements/description/)
150. 逆波兰表达式求值
python有坑
- .isdigit()只能判断正数没办法判断负数-------用int转换并加try except过掉计算符
- python里 / 是向下除而不是向0除--------先浮点除,再取整
字符串转计算,自己定义一个函数
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
stack = []
val = 0
for s in tokens:
try:
stack.append(int(s))
except:
val = self.calculate(stack.pop(), s, stack.pop())
stack.append(val)
if stack:
val = stack.pop() # 单独考虑只有一个元素的时候
return val
def calculate(self, b: int, s: str, a: int) -> int:
if s == '+':
return a + b
if s == '-':
return a - b
if s == '*':
return a * b
if s == '/':
return int(a/b)
239. 滑动窗口最大值
单调队列头
一个单调减的队列,头始终保存最大的,后面加进来的如果比前面的大,就把前面的都弹出,也就是说,这个数进来以后,之前的小一点的数都不可能成为最大值。
为啥是递减,比如5 3 1 当窗口移动时,5弹出,3就是预备最大的,所以是递减的。
用队列模拟滑动窗口没啥意义,用队列维护可能最大值(预备潜在队员)才有意义.
卡在当滑动窗口时,怎么去掉已经出去的那个元素。分析一下,这个元素有要么比之前的maxnum小,要么等于maxnum。如果比maxnum小,有两种可能,在maxNum之前(排除,因为如果在之前,就会被pop),在之后(排除,之后的话不会有maxnum这个数比他早pop),所以只有唯一一种情况就是等于maxnum,因为比maxnum小的话在之前肯定就被pop掉了。
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
queue = deque()
queue.append(nums[0])
maxnum = nums[0]
ans = []
for i in range(1, k):
maxnum = self.maxNumGet(nums[i], queue)
ans.append(maxnum)
# print('1:',maxnum)
for i in range(len(nums)-k):
# print(nums[i], maxnum)
if queue and nums[i] == queue[0]: # 其余情况不用管,之前插入的时候已经处理掉了
queue.popleft()
# print(queue)
maxnum = self.maxNumGet(nums[i+k], queue)
# print(maxnum,queue)
ans.append(maxnum)
return ans
def maxNumGet(self, val:int, queue: deque) -> int:
# 插入
if not queue: # 当k = 1,queue到这里会变空
return val # 只返回值,queue就是空
if val > queue[0]: # 等于的时候也留维持期长的,但是如果有两个,遇到第一个就会被删掉
queue.clear()
queue.append(val)
elif val <= queue[-1]:
queue.append(val)
else:
while val > queue[-1]: # 小于等于都添加,比如 777,这样第一个删掉之后,后面的还在
queue.pop()
queue.append(val)
# 获取
return queue[0]
灵神思路
单调队列套路
入(元素进入队尾,同时维护队列单调性)
出(元素离开队首)
记录/维护答案(根据队首)
单调队列 = 单调栈 + 滑动窗口(移除队首,左指针后移)
他的队列是维护列表的下标
思考:为啥他可以对i x enumerate,当i - q[0] >= k时,弹出。因为只有一种可能需要弹出,就是最大值在队首的时候,后面的都比他小,所以长度就等于滑动窗口的时候,弹出那个出窗口的值也就是当下的最大值。
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
ans = []
q = deque()
for i, x in enumerate(nums):
# 入
while q and nums[q[-1]] < x:
q.pop()
q.append(i)
# 出
if i - q[0] >= k:
q.popleft()
# 取最大值
if i >= k - 1:
ans.append(nums[q[0]])
return ans # 注意返回的位置
347.前 K 个高频元素
- 要统计元素出现频率
- 对频率排序
- 找出前K个高频元素
优先级队列 前k个最大或者最小
如果用map来存放频率,再排序得到前k个,时间复杂度是nlog(n)。如果有一个数据结构能够只维护k个 大顶堆(根节点大) 小顶堆 (堆是一棵完全二叉树)
当push一个元素时,就得pop一个堆顶。如果是大顶堆,pop堆顶是最大的,最后留下的是最小的k个数,如果是小顶堆,顶上小,都弹出,最后留下大的,也就是频率最大的k个,再在map中找到对应的key就是数.
再来复习一下字典赋值
if x in mapping:
mapping[x] += 1
else:
mapping[x] = 1
# 或者直接
mapping[x] = mapping.get(x, 0) + 1
字典遍历是.item(),列表元组是enumerate(),两者都返回一个元组(key/索引,val)
堆 用heappush heappop
import heapq
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
# 统计频率
mapping = {}
for i, x in enumerate(nums):
mapping[x] = mapping.get(x, 0) + 1
# 小顶堆
minque = []
for num, freq in mapping.items():
heapq.heappush(minque,(freq, num))
if len(minque) > k:
heapq.heappop(minque)
ans = [num for freq, num in minque] # 直接返回
return ans
heapq.heappush(pri_que, (freq, key)) 中heapq是小顶堆,时间复杂度是log(k),如果想大顶堆,可以把freq取负数。这个是对元组的第一个数进行排序 (freq, key)。
时间复杂度: O(nlogk)
空间复杂度: O(n)