LeetCode刷题小记 六、【栈与队列】

1.栈与队列

文章目录

    • 1.栈与队列
    • 写在前面
      • 1.1栈与队列理论基础
      • 1.2用栈实现队列
      • 1.3用队列实现栈
      • 1.4有效的括号
      • 1.5删除字符串中的所有相邻重复项
      • 1.6逆波兰表达式求值
      • 1.7滑动窗口最大值
      • 1.8前K个高频元素
    • Reference

写在前面

本系列笔记主要作为笔者刷题的题解,所用的语言为Python3,若于您有助,不胜荣幸。

1.1栈与队列理论基础

栈[stack]是一种先进后出逻辑的线性数据结构。栈的常用操作如表所示

方法描述时间复杂度
push()元素入栈 O ( 1 ) \mathcal{O}(1) O(1)
pop()栈顶元素出栈 O ( 1 ) \mathcal{O}(1) O(1)
peek()访问栈顶元素 O ( 1 ) \mathcal{O}(1) O(1)

队列[queue]是一种先进先出逻辑的线性数据结构。我们将队列的头部称为“队首”,尾部称为“队尾”,将把元素加入队尾的操作称为“入队”,删除队首元素的操作称为“出队”。为了统一我们采用和栈相同的命名方式

方法描述时间复杂度
push()元素入队,即将元素添加到队尾 O ( 1 ) \mathcal{O}(1) O(1)
pop()队首元素出队 O ( 1 ) \mathcal{O}(1) O(1)
peek()访问队首元素 O ( 1 ) \mathcal{O}(1) O(1)

1.2用栈实现队列

232. 用栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(pushpoppeekempty):

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

思路:使用两个栈来实现一个队列,我们需要一个栈充当in,负责接收元素,另一个栈充当out,负责弹出元素,每当我们需要弹出元素的时候,我们就将in中的所有元素弹出并加入到out中,然后再从out中弹出栈顶元素。

class MyQueue:def __init__(self):self.stack_in: List = []self.stack_out: List = []def push(self, x: int) -> None:while self.stack_out:       # 将out栈中的元素恢复到in栈中self.stack_in.append(self.stack_out.pop())self.stack_in.append(x)     # 添加新元素def pop(self) -> int:if self.empty():return Noneif self.stack_out:return self.stack_out.pop()else:while self.stack_in:self.stack_out.append(self.stack_in.pop())return self.stack_out.pop()def peek(self) -> int:res: int = self.pop()self.stack_out.append(res)return resdef empty(self) -> bool:return not self.stack_in and not self.stack_out     # 只要in或out含有元素,说明队列不为空

1.3用队列实现栈

225. 用队列实现栈

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppopempty)。

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false

思路:我们可以使用两个队列来实现一个栈,或者使用一个队列来实现一个栈。两种方法都是可以的

解法一:使用一个队列

# 使用一个队列来实现栈
from collections import deque
class MyStack:def __init__(self):self.queue: deque = deque()self.size: int = 0def push(self, x: int) -> None:self.queue.append(x)self.size += 1def pop(self) -> int:if self.empty():return Nonefor _ in range(self.size-1):self.queue.append(self.queue.popleft())self.size -= 1return self.queue.popleft()def top(self) -> int:if self.empty():return Noneans: int = self.pop()self.push(ans)return ansdef empty(self) -> bool:return not self.queue

解法二:使用两个队列

from collections import deque
class MyStack:def __init__(self):self.que_in: deque = deque()self.que_out: deque = deque()def push(self, x: int) -> None:while self.que_out:         # 将out中的元素恢复到in中self.que_in.append(self.que_out.popleft())self.que_in.append(x)       # 添加新的元素def pop(self) -> int:"""1. 首先确认不空2. 因为队列的特殊性,FIFO,所以我们只有在pop()的时候才会使用queue_out3. 先把queue_in中的所有元素(除了最后一个),依次出列放进queue_out4. 交换in和out,此时out里只有一个元素5. 把out中的pop出来,即是原队列的最后一个tip:这不能像栈实现队列一样,因为另一个queue也是FIFO,如果执行pop()它不能像stack一样从另一个pop(),所以干脆in只用来存数据,pop()的时候两个进行交换"""if self.empty():return Nonefor _ in range(len(self.que_in)-1):     # 保存前n-1个元素self.que_out.append(self.que_in.popleft())self.que_out, self.que_in = self.que_in, self.que_out   # 这里很重要return self.que_out.popleft()def top(self) -> int:if self.empty():return Nonefor _ in range(len(self.que_in)-1):     # 保存前n-1个元素self.que_out.append(self.que_in.popleft())self.que_out, self.que_in = self.que_in, self.que_outans: int = self.que_out.popleft()self.que_in.append(ans)return ansdef empty(self) -> bool:return not self.que_in and not self.que_out

使用两个队列,用存储空间来换取时间,明显看出这样的执行速度更快。

1.4有效的括号

20. 有效的括号

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

思路:首先我们需要找到有哪些不匹配的情况,其实这道题目一共只有三种不匹配的情况,分别是:

  • 情况1:字符串左边存在多余的括号([{}]()
  • 情况2:字符串中间存在不匹配的括号[{(]}]
  • 情况3:字符串右边存在多余的括号[{}]()))

针对这三种情况我们分别来进行处理即可。

class Solution:def isValid(self, s: str) -> bool:stack: List = []if len(s) % 2 != 0:     # 剪枝(可省略)return Falsefor char in s:if char == '(':stack.append(')')elif char == '[':stack.append(']')elif char == '{':stack.append('}')elif not stack or char != stack[-1]:        # 处理情况二中间不匹配,或者情况三右边有多余的元素return Falseelse:stack.pop()return True if not stack else False             # 处理情况一左边有多余的元素

1.5删除字符串中的所有相邻重复项

1047. 删除字符串中的所有相邻重复项

给出由小写字母组成的字符串 S重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

思路:这是一道典型的使用栈来完成的题目,我们只需要判断入栈的元素是否等于栈顶元素,如果等于则表明这是一对需要删除的元素,我们只需要移除栈顶元素即可,最后返回由栈中所有元素构成的字符串即可。

class Solution:def removeDuplicates(self, s: str) -> str:stack: List[str] = []for c in s:if stack and c == stack[-1]:    # 如果当前入栈的元素等于栈顶元素,且栈不为空,则删除栈顶元素stack.pop()else:stack.append(c)return ''.join(stack)

1.6逆波兰表达式求值

150. 逆波兰表达式求值

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

思路:什么是逆波兰表达式呢?这其实是一种方便计算机运算的存储方式,逆波兰表达式其实是相当于二叉树的后序遍历,本题中每一个子表达式要得出一个结果,然后拿这个结果再进行运算,这就非常像一个字符串匹配的问题,我们就可以用栈这种数据结构来进行处理。注意:第一个弹出的数字应该在运算符的后面

class Solution:       def evalRPN(self, tokens: List[str]) -> int:op_map = {'+': add, '-': sub, '*': mul, '/': lambda x, y: int(x/y)}stack: List[int] = []for c in tokens:if c in ['+', '-', '*', '/']:num2 = stack.pop()num1 = stack.pop()stack.append(op_map[c](num1, num2)) # 第一个出来的在运算符后面else:stack.append(int(c))return stack[-1]

1.7滑动窗口最大值

239. 滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值

思路:维护一个单调队列,如果push()进的值大于当前队列的队首位置的,则将队列清空,再将这个值push()进来,这样就维护了一个单调递减的队列,并且每次访问队首位置就能够访问队列中的最大值,那我们应该如何做pop()呢?通常来说做pop()不需要传入任何的值,但是这里我们可以判断我们当前需要pop()的值是否和队首的位置相等,如果相等则表明这是需要弹出的元素:

  1. pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
  2. push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止

保持如上规则,每次窗口移动的时候,只要问que.front()就可以返回当前窗口的最大值。

from collections import deque
class MyQueue:def __init__(self):	# 单调队列,从大到小self.que: deque = deque()#每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。#同时pop之前判断队列当前是否为空。def pop(self, value):if self.que and self.que[0] == value:self.que.popleft()#如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。#这样就保持了队列里的数值是单调从大到小的了。def push(self, value):while self.que and value > self.que[-1]:self.que.pop()self.que.append(value)def front(self):return self.que[0]class Solution:def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:result: List[int] = []queue: MyQueue = MyQueue()for i in range(k):queue.push(nums[i])result.append(queue.front())for i in range(k, len(nums)):queue.pop(nums[i-k])    # 判断进入上个队列的首个元素还存在吗?如果存在就表明这个元素是最大的值,并且没有被pop掉,就手动popleft掉这个元素queue.push(nums[i])result.append(queue.front())return result

1.8前K个高频元素

347. 前 K 个高频元素

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

思路:先使用哈希法得到一个可供查询的map,然后获取这个map中value值的前k个即可,但是我们无需对整个map都进行排序,而是我们只维护一个大小为k的有序区间。这就涉及到了大顶堆和小顶堆这样的数据结构。这里涉及到选择小顶堆还是大顶堆的问题,如果我们选择大顶堆的话,每次弹出的元素都是最大的元素,这样我们就把最大的元素都弹出了,只保留了较小的元素,相反如果我们选择小顶堆的话,每次弹出都是弹出最小的元素,这样就保留了加大的元素。

import heapq
class Solution:def topKFrequent(self, nums: List[int], k: int) -> List[int]:elem_map: dict = {}for elem in nums:elem_map[elem] = elem_map.get(elem, 0) + 1# 对频率进行排序,定义一个小顶堆pri_que = []for key, value in elem_map.items():heapq.heappush(pri_que, (value, key))   # heapq按照tuple中的第一个元素来进行排序if len(pri_que) > k:    #如果堆的大小大于了K,则队列弹出,保证堆的大小一直为kheapq.heappop(pri_que)return [key for value, key in pri_que]      # 对返回的顺序没有要求,我们可以不用每次都进行弹出
import heapq
class Solution:def topKFrequent(self, nums: List[int], k: int) -> List[int]:elem_map: dict = {}for elem in nums:elem_map[elem] = elem_map.get(elem, 0) + 1# 对频率进行排序,定义一个小顶堆pri_que: List[int] = []for key, value in elem_map.items():heapq.heappush(pri_que, (value, key))   # heapq按照tuple中的第一个元素来进行排序if len(pri_que) > k:    #如果堆的大小大于了K,则队列弹出,保证堆的大小一直为kheapq.heappop(pri_que)res: List[int] = [0] * kfor i in range(k):          # 按照频率顺序进行弹出res[k-i-1] = heapq.heappop(pri_que)[1]return res

Reference

[1] Hello 算法
[2] 代码随想录

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/717143.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分布式基础 --- Leader election

分布式基础 --- Leader election 为什么需要leader electionRing electionBully Algorithm 为什么需要leader election 在一组集群中, 需要选出一个leader来承担一些特别的任务, 比如 协调和控制系统操作:领导者负责协调和控制整个分布式系统的操作。它可以接收和处…

one4all 排坑记录

one4all 排坑记录 任务踩坑回顾动作踩坑动作踩坑动作新一步测试Habitat-sim 测试habitat-lab继续ONE4ALL 任务 看了《One-4-All: Neural Potential Fields for Embodied Navigation》这篇论文,感觉挺有意思,他也开源了代码。视觉语言导航是我一直想做的…

windows上elasticsearch的ik分词器的安装

下载 下载地址 在elasticsearch下的plugins文件夹下创建ik的文件夹 下载的ik压缩包解压到plugins/ik 重启elasticsearch 验证 http://ip:9200/_cat/plugins

python笔记_运算符优先级

运算符描述算术运算符&#xff08;x&#xff09;括号内优先级最高**乘方 * / // % 乘 矩阵乘 除 整除 取余 _ 加 减 位运算符 >> << 右移 左移 &按位与^按位异或|按位或比较运算符 in not in is is not < < > > ! 判断两个变量是否相同 判…

SpringBoot3-核心原理

1. 事件和监听器 1. 生命周期监听 场景&#xff1a;监听应用的生命周期 1. 监听器-SpringApplicationRunListener 自定义SpringApplicationRunListener来监听事件&#xff1b; 编写SpringApplicationRunListener 实现类在 META-INF/spring.factories 中配置 org.springfram…

【蓝桥杯】错误票据

今天是2024年3月1号&#xff0c;蓝桥杯比赛还有一个月的时间&#xff0c;虽说自己不指望拿奖吧&#xff0c;但是还是有些莫i名的焦虑&#xff0c;这道题目都做不出来&#xff0c;感觉自己真的有点菜啊&#xff01;但是还好啦&#xff0c;我觉得是因为我没有题感&#xff0c;慢慢…

spring boot 整合 minio存储 【使用篇】

导入依赖 <!--minio--><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.0.3</version></dependency> yml配置&#xff08;默认配置&#xff09; max-file-size: 200MB 设置文件最大…

华为od机试C卷-开源项目热度榜单

1、题目描述 某个开源社区希望将最近热度比较高的开源项目出一个榜单&#xff0c;推荐给社区里面的开发者。 对于每个开源项目&#xff0c;开发者可以进行关注(watch)、收藏(star)、fork、提issue、提交合并请求(MR)等。 数据库里面统计了每个开源项目关注、收藏、fork、issue…

微服务API网关---APISIX

最近在做微服务调研&#xff0c;看到了apisix这个网关&#xff0c;于是进行了初步了解一下。 微服务是指&#xff0c;将大型应用分解成多个独立的组件&#xff0c;其中每个组件都各自的负责对应项目。 系统的架构大致经历了&#xff1a;单体应用架构–> SOA架构 -->微服务…

Linux多线程服务端编程:使用muduo C++网络库 学习笔记 附录D 关于TCP并发连接的几个思考题与试验

前几天作者在新浪微博上出了两道有关TCP的思考题&#xff0c;引发了一场讨论&#xff08;http://weibo.com/1701018393/eCuxDrtaONn&#xff09;。 第一道初级题目是&#xff1a;有一台机器&#xff0c;它有一个IP&#xff0c;上面运行了一个TCP服务程序&#xff0c;程序只侦听…

StarRocks实战——松果出行实时数仓实践

目录 一、背景 二、松果出行实时OLAP的演进 2.1 实时数仓1.0的架构 2.2 实时数仓2.0的架构 2.3 实时数仓3.0的架构 三、StarRocks 的引入 四、StarRocks在松果出行的应用 4.1 在订单业务中的应用 4.2 在车辆方向的应用 4.3 StarRocks “极速统一” 落地 4.4 StarRoc…

Lambda、Function、StreamAPI详解

目录 1、Lambda 2、Function 3、StreamAPI 中间操作&#xff1a;Intermediate Operations 终止操作&#xff1a;Terminal Operation 1、Lambda Java8语法糖&#xff1a;参数列表 箭头 方法体 package com.atguiggu.lambda;import java.util.*; import java.util.funct…

分布式ID生成系统之雪花算法详解

在当今的云计算和微服务架构盛行的时代&#xff0c;分布式系统已成为软件开发的重要组成部分。随着系统规模的扩大和业务的复杂化&#xff0c;对数据一致性和唯一性的要求也越来越高&#xff0c;尤其是在全局唯一标识符&#xff08;ID&#xff09;的生成上。因此&#xff0c;分…

代码随想录算法训练营Day48 | 121.买卖股票的最佳时机、122.买卖股票的最佳时机 II

121.买卖股票的最佳时机 &#xff08;想写动态规划写着写着变成贪心了&#xff09; 半贪心半动规&#xff1a; int maxProfit(vector<int>& prices) {vector<int> dp(prices.size(), 0);int minVal prices[0];for (int i 1; i < prices.size(); i) {//…

yolov5训练太慢的解决方案

问题原因 训练太慢大多是因为没有安装CUDA和pytorch&#xff0c;导致的只有cpu在跑&#xff0c;显卡没跑 这就是很典型的。 解决方案 第一步&#xff1a;安装CUDA 在本机上面安装CUDA,记住只有N卡可以安装&#xff0c;一开始的电脑是自带CUDA的。 如果不是自带的CUDA&…

Apache Paimon Flink引擎解析

Paimon 支持 Flink 1.17, 1.16, 1.15 和 1.14&#xff0c;当前 Paimon 提供了两类 Jar 包&#xff0c;一类支持数据读写&#xff0c;另一类支持其它操作&#xff08;compaction&#xff09; Version Type Jar Flink 1.18 Bundled Jar paimon-flink-1.18-0.7…

SentenceTransformer简单使用

SentenceTransformer简单使用 1 SentenceTransformer介绍 SentenceTransformer主要用于对句子、文本和图像进行嵌入。可用于文本和图像的相似度对比查找等 # SentenceTransformer官网地址 https://www.sbert.net/# 安装SentenceTransformer pip install -U sentence-transfo…

求数字的每一位之和

求数字的每一位之和 题目描述&#xff1a;解法思路&#xff1a;解法代码&#xff1a;运行结果&#xff1a; 题目描述&#xff1a; 输入一个整数m&#xff0c;求这个整数m的每⼀位之和&#xff0c;并打印。 测试1&#xff1a; 输⼊&#xff1a;1234 输出&#xff1a;10 测试2&…

土壤侵蚀量化评估

根据之前的文章,已经算出了R、K、LS、C、P 现在计算土壤侵蚀,将几个前期制作好的因子的TIFF文件,用栅格计算器相乘 发现局部地区存在轻度侵蚀,大部分区域是微度侵蚀 然后对比了一下范围 其中的几个因子都在文献范围内,说明计算结果并未出错,可能就是研究区正常范围和结…

6020一拖二快充线:手机充电的革命性创新

在快节奏的现代生活中&#xff0c;手机已不仅仅是一个通讯工具&#xff0c;更是我们工作、学习和娱乐的得力助手。然而&#xff0c;手机的电量问题一直是困扰着我们的难题。为了解决这个问题&#xff0c;市场上出现了一种名为“一拖二快充线”的充电设备&#xff0c;它不仅具备…