五、栈与队列(2)

150. 逆波兰表达式求值

python有坑

  1. .isdigit()只能判断正数没办法判断负数-------用int转换并加try except过掉计算符
  2. 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 个高频元素

  1. 要统计元素出现频率
  2. 对频率排序
  3. 找出前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)

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值