当前位置: 首页 > news >正文

代码随想录算法训练营第十天(栈与队列篇)|Leetcode150逆波兰表达式求解,Leetcode239滑动窗口最大值,Leetcode347前K个高频元素

Leetcode 150 逆波兰表达式求解

题目链接: 逆波兰表达式求解

给定一个字符串类型的数组,其中按照逆波兰表示法记录了算术表达式,求解该算术表达式最终的结果。

Example 1:Input: tokens = ["2","1","+","3","*"]
Output: 9
Explanation: ((2 + 1) * 3) = 9Example 2:Input: tokens = ["4","13","5","/","-"]
Output: 6
Explanation: (4 - (13 / 5)) = 2

思路: 由于逆波兰表示法的特性(一次计算由两个数字跟一个算术符号完成),我们可以利用栈后入先出的特性,每当遇到数字时就压栈,当遇到算术符号时就弹出两个数字进行计算。重复上述操作直至完成数组中所有元素的遍历,随后取出栈中剩下的元素即可。

具体代码实现:

class Solution:def evalRPN(self, tokens: List[str]) -> int:myStack = []operators = '+-*/'for token in tokens:if token not in operators:myStack.append(token)else:num2 = int(myStack.pop())num1 = int(myStack.pop())if token == '+':myStack.append(num1 + num2)elif token == '-':myStack.append(num1 - num2)elif token == '*':myStack.append(num1 * num2)elif token == '/':myStack.append(num1 // num2)return int(myStack.pop())

Leetcode 239 滑动窗口最大值

题目链接: 滑动窗口最大值

给定一个数组 nums 和一个整数 k,大小为 k 的滑动窗口从数组的左侧移动到右侧,一次移动一个单位,返回每次移动时滑动窗口的最大值。

Example 1:Input: nums = [1,3,-1,-3,5,3,6,7], k = 3
Output: [3,3,5,5,6,7]
Explanation:
Window position                Max
---------------               -----
[1  3  -1] -3  5  3  6  7       31 [3  -1  -3] 5  3  6  7       31  3 [-1  -3  5] 3  6  7       51  3  -1 [-3  5  3] 6  7       51  3  -1  -3 [5  3  6] 7       61  3  -1  -3  5 [3  6  7]      7

思路: 随着滑动窗口的移动,我们的问题关键在于,如何高效地求出一个窗口内的最大值。若通过暴力方法直接遍历窗口找出最大值,则整体时间复杂度为 O(n*k)。我们需要更加高效的方法。

回看题设我们发现,随着滑动窗口的移动,旧的元素被移出滑动窗口,而新的元素被移入滑动窗口,这符合队列的特性,我们可以通过一个队列管理滑动窗口中的元素。进一步进行思考,是否有什么操作能够帮助我们快速找出队列中的最值?答案是单调队列。在向队列中添加元素时,我们维护一个单调队列,这样方便我们快速找出最值。此题中我们使用单调递减队列,这样队头就是当前滑动窗口的最大值。

具体代码实现

class Solution:def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:# 此处采用双向队列而非队列,因为维护单调队列时需要从尾部移除元素from collections import dequeresult = []myQueue = deque(maxlen=k)# 先将前k个元素插入队列中# 再移动滑动窗口,将遍历到的当前数组中的元素插入队列中。# 若当前待插入队列的元素比队列中其前一个元素大,则将前一个元素弹出队列,确保了队列的单调性# 重复此操作,此时队列中队头的元素即为队列中元素最大值for i in range(k):while myQueue and myQueue[-1] < nums[i]:myQueue.pop()myQueue.append(nums[i])result.append(myQueue[0])for i in range(k, len(nums)):# 当队头元素与应当移出滑动窗口的元素相同时,将该元素移出队头if myQueue and nums[i-k] == myQueue[0]:myQueue.popleft()while myQueue and myQueue[-1] < nums[i]:myQueue.pop()myQueue.append(nums[i])result.append(myQueue[0])return result

时间复杂度: O(n)

Leetcode 347 前K个高频元素

题目链接: 前K个高频元素

给定一个数组 nums 找出其中出现的前K个出现频次最高的元素。

Example 1:Input: nums = [1,1,1,2,2,3], k = 2
Output: [1,2]Example 2:Input: nums = [1], k = 1
Output: [1]

思路: 为了找出给定数组中前K个高频元素,我们需要:

  1. 统计数组中各个元素出现的频次
  2. 对频次进行排序
  3. 找出其中出现的前K个出现频次最高的元素

对于任务1,我们可以通过 map 映射记录各个元素出现的频次。
对于任务2,在具体实现上,我们既可以通过优先级队列对频次进行排序,也可以直接对频次进行排序。

优先级队列是一种抽象数据类型,每个元素带有一个 "优先级",每次弹出/访问的始终是当前优先级最高/最低的元素。在本题中,由于我们要找的是频率最高的元素,因此此处的优先级为元素出现的频次。优先级队列的具体实现一般为堆(在python中默认为小顶堆),因为堆从堆头取出元素, 从堆底添加元素,与队列从队头取元素,从队尾添加元素一致。

具体代码实现

# 使用优先级队列求解
from typing import List
import heapq
class Solution:def topKFrequent(self, nums: List[int], k: int) -> List[int]:myMap = {}for num in nums:myMap[num] = myMap.get(num, 0) + 1prior_queue = []for num, freq in myMap.items():# 插入堆时,将一个二元组(priority, item)插入堆中,此处将元素出现的频次作为优先级heapq.heappush(prior_queue, (freq, num))if len(prior_queue) > k:heapq.heappop(prior_queue)result = [0] * kfor i in range(k-1, -1, -1):result[i] = heapq.heappop(prior_queue)[1]  # 倒序取出元素return result
# 使用字典求解
from collections import defaultdict
class Solution:def topKFrequent(self, nums: List[int], k: int) -> List[int]:# 首先统计各个元素出现的频次freqMap = {}for num in nums:freqMap[num] = freqMap.get(num, 0) + 1# 随后,再将频次作为key,出现对应次数的元素放在一个数组中,作为valueidxMap = defaultdict(list)for num in freqMap:idxMap[freqMap[num]].append(num)# 对出现的频次进行排序freq = list(idxMap.keys())sortedFreq = sorted(freq)result = []count = 0while count < k:# 将出现频次最高的元素放入结果中,重复这一操作直到找到的元素大于等于k个result.extend(idxMap[sortedFreq[-1]])count += len(idxMap[sortedFreq[-1]])sortedFreq.pop()return result[0:k]

复杂度分析: 时间复杂度: O(nlogn), 空间复杂度: O(n)

http://www.sczhlp.com/news/13871/

相关文章:

  • orval初步使用
  • 状压DP 详解教程 简单易学(bushi
  • 使用URLSearchParams 优雅的获取URL携带的参数
  • P2985 [USACO10FEB] Chocolate Eating S 题解 二分答案
  • 使用 popcount 使得两个集合完全相等的最小操作次数
  • scanf(%s) 的4095字节的长度限制
  • 【渲染流水线】[几何阶段]-[屏幕映射]以UnityURP为例
  • 图表接口按日期的统计查询开发
  • 守护遗留系统的安全挑战与应对策略
  • Python3 简介
  • 【综述】VLA模型:概念、进展、应用与挑战
  • 七天零基础学java(第七天)--
  • 02011801 泛型01-什么是泛型、泛型类、构造类型、类型参数、where子句
  • 在K8S中,Service分发后端的策略是什么?
  • 井字棋
  • Js 面向对象-Class补充
  • webpack4项目中,使用@zip.js/zip.js(2.7.72版本)解析zip包 报错 unexpected token import.meta.url
  • Manacher 做题记录
  • 5.7 文件的修改
  • 2025年8月17日15:31:09
  • 家庭配电箱内的开关有多种类型,每种开关的作用、分类及常见用途都不尽相同。下面是详细的分类说明以及表格化的展示:
  • HS_fu3的语录
  • 在K8S中,外部如何访问集群内的服务?
  • CSP-S模拟13
  • 你好, 再见 ! 董小姐
  • CSP-S2025模拟7-13
  • 模拟费用流入门
  • 合页作为一种常见的连接元件,其发展历程与建筑、家具、机械等领域的需求密切相关。以下是合页发展的时间线,详细说明了其主要的发展阶段:
  • ROS2 学习(一)——节点的概念
  • 关于柜门铰链的发展时间线,它经历了多个阶段的创新与进步,从最早的简单支撑结构到现代智能调节功能的集成。以下是详细的时间线和各阶段的发展说明: