【Day10】LeetCode:150. 逆波兰表达式求值,239. 滑动窗口最大值,347. 前 K 个高频元素

LeetCode:150. 逆波兰表达式求值

https://leetcode.cn/problems/evaluate-reverse-polish-notation/

思路

如果遇到数字就压入栈中,如果遇到运算符就弹出栈顶两个元素进行运算(先弹出来的是运算符右边的数字,后弹出来的是运算符左边的数字),将结果压入栈中,最后栈中剩下的数字就是结果。

解答

class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        stk = []
        for token in tokens:
            if token == '+' or token == '-' or token == '*' or token == '/': # 如果是运算符就弹出栈顶两个元素进行运算
                right_num = stk.pop()
                left_num = stk.pop()
                if token == '+':
                    result_num = left_num + right_num
                elif token == '-':
                    result_num = left_num - right_num
                elif token == '*':
                    result_num = left_num * right_num
                else:
                    result_num = int(left_num / right_num)
                stk.append(result_num)
            else: # 如果是数字就压入
                stk.append(int(token))
        return stk[0]

LeetCode:239. 滑动窗口最大值

https://leetcode.cn/problems/sliding-window-maximum/

思路

暴力法

每次移动滑动窗口,就遍历窗口中的元素,找出最小值并记录。
时间复杂度为 O ( n × k ) O(n \times k) O(n×k) ,但当 k k k 较大的时候,时间复杂度接近 O ( n 2 ) O(n^2) O(n2)

单调递减的双端队列

维护一个单调递减的双端队列,队列中存储元素下标,其对应值是可能成为最大值的元素。
遍历数组 nums 时:

  • 移除队列中不在当前窗口内的下标(因此队列中记录的下标会更好进行判断);
  • 移除队列中所有值小于等于当前元素的下标,保持单调递减;
  • 将当前下标加入队列;
  • 当窗口形成后,记录当前窗口的最大值。

例如,对于 nums = [1,3,-1,-3,5]k = 3

  1. i = 0, num = 1deque 队列为空,直接压入对应的坐标0,得到 deque = [0]
  2. i = 1, num = 3deque = [0] ,因为 3 > 1 3 > 1 3>1 ,3更有可能成为最大值,弹出1对应的坐标0,压入3对应的坐标1,得到 deque = [1]
  3. i = 2, num = -1deque = [1] ,因为 − 1 < 3 -1 < 3 1<3 ,直接舍弃,此时已经形成窗口(i == k - 1)且此后窗口一直存在直到遍历结束,将队列顶部元素对应的值记录到结果,得到 results = [3]
  4. i = 3, num = -3deque = [1] ,因为 − 3 < 3 -3 < 3 3<3 ,直接舍弃,将队列顶部元素对应的值记录结果,得到 results = [3, 3]
  5. i = 4, num = 5deque = [1] ,因为 5 > 3 5 > 3 5>3 ,5更有可能成为最大值,弹出3对应的坐标1,压入5对应的坐标4,得到 deque = [4] ,将队列顶部元素对应的值记录结果,得到 results = [3, 3, 5]
  6. 遍历结束,返回答案 results = [3, 3, 5]

解答

from collections import deque
class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        dq = deque() # 维护一个单调的双端队列,存储元素下标,维持下标对应的值单调递减
        result = []
        for i, num in enumerate(nums):
            if dq and dq[0] < i - k + 1: # 移除队列中不在当前窗口内的下标
                dq.popleft()

            while dq and nums[dq[-1]] <= num: # 移除队列中所有值小于等于当前元素的下标,保持单调递减
                dq.pop()
            dq.append(i) # 将当前下标加入队列

            if i >= k - 1: # 当窗口形成后,记录当前窗口的最大值
                result.append(nums[dq[0]])
        return result

时间复杂度: O ( n ) O(n) O(n) ,每个元素最多入队和出队一次。
空间复杂度: O ( k ) O(k) O(k) ,队列最多存储 k k k 个下标。

LeetCode:347. 前 K 个高频元素

https://leetcode.cn/problems/top-k-frequent-elements/description/

思路

python中的 heapq 模块可以提供小顶堆。

  1. 首先我们先统计每个数字出现的频次,得到 countMap
  2. 维护一个长度为 k 的小顶堆 heapMin 。遍历 countMap ,以 count 为排序标准,将元组 (count, num) 压入堆中。如果堆已满,则与堆顶元素进行比较:如果堆顶元素更小,则弹出堆顶元素,将元组 (count, num) 压入堆中;否则跳过。
  3. 最后留在堆中的就是频率最高的 k 个数字。

解答

import heapq
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        results = []
        # 统计数字出现的频率
        countMap = {}
        for num in nums:
            if num in countMap:
                countMap[num] += 1
            else:
                countMap[num] = 1

        # 构建一个小顶堆
        heapMin = []
        for num, count in countMap.items():
            if len(heapMin) < k: # 如果堆内元素的数量小于k,则直接压入
                heapq.heappush(heapMin, (count, num))
            else: # 如果堆已满,则和堆顶元素进行比较
                # 如果当前元素比堆顶元素大,则插入
                if count > heapMin[0][0]:
                    heapq.heappop(heapMin)
                    heapq.heappush(heapMin, (count, num))
                else: # 如果当前元素比堆顶元素小,则丢弃
                    continue

        for count, num in heapMin:
            results.append(num)

        return results
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值