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

栈与队列-

学习计划:3个月内算法小成,完成leetcode hot100

当前进度:学完数组、链表、哈希表、字符串、双指针

刷题语言:Python

时间:2025/04/02-2025/05/17

六、栈与队列

学习链接:代码随想录

1、栈与队列理论基础

  • 栈:先进后出
  • 队列:先进后出

python基础:python中没有内置栈但是有列表List,队列使用deque

stack = []         #定义一个栈
stack.append(1)    #push进1,stack=[1]
stack.append(2)    #push进2,stack=[1,2]
out = stack.pop()  #pop栈顶,out=2,stack=[1](后进先出)que = deque()      #定义一个队列
que.append(1)      #push进1,que=[1]
que.append(2)      #push进2,que=[1,2]
que.append(3)      #push进3,que=[1,2,3]
out = que.pop()    #pop队列,out=3,que=[1,2]
out = que.popleft()#popleft队列,out=1,que=[2]

2、用栈实现队列

使用栈实现队列的下列操作:

  • push(x) -- 将一个元素放入队列的尾部。
  • pop() -- 从队列首部移除元素。
  • peek() -- 返回队列首部的元素。
  • empty() -- 返回队列是否为空。

说明:

  • 你只能使用标准的栈操作 -- 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
  • 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。

思路:

        这是一道模拟题,不涉及到具体算法,考察的就是对栈和队列的掌握程度。使用栈来模拟队列的行为,如果仅仅用一个栈,是一定不行的,所以需要两个栈一个输入栈,一个输出栈,这里要注意输入栈和输出栈的关系。

        在push数据的时候,只要数据放进输入栈就好,但在pop的时候,操作就复杂一些,输出栈如果为空,就把进栈数据全部导入进来(注意是全部导入),再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。

class MyQueue:def __init__(self):#in负责push,out负责popself.stack_in = []self.stack_out = []def push(self, x: int) -> None:#有新元素进来,放入inself.stack_in.append(x)def pop(self) -> int:#移除元素if self.empty(): #先判断空return Noneif self.stack_out: #如果stack_out有元素,从stack_out出return self.stack_out.pop()else:              #如果stack_out没有元素,先将stack_in一个个pop到stack_out,再从stack_out出for i in range(len(self.stack_in)):self.stack_out.append(self.stack_in.pop())return self.stack_out.pop()def peek(self) -> int:#获取队列开头tmp = self.pop()self.stack_out.append(tmp)  #pop出来后记得放回去return tmpdef empty(self) -> bool:#只有in或out有一个有元素就不为空return not (self.stack_in or self.stack_out)# Your MyQueue object will be instantiated and called as such:
# obj = MyQueue()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.peek()
# param_4 = obj.empty()

3、用队列实现栈

使用队列实现栈的下列操作:

  • push(x) -- 元素 x 入栈
  • pop() -- 移除栈顶元素
  • top() -- 获取栈顶元素
  • empty() -- 返回栈是否为空

注意:

  • 你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的。
  • 你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
  • 你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。

思路:一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时再去弹出元素就是栈的顺序了。

class MyStack:def __init__(self):self.que = deque()def push(self, x: int) -> None:self.que.append(x)def pop(self) -> int:if self.empty():return Nonefor i in range(len(self.que)-1):self.que.append(self.que.popleft())return self.que.popleft()def top(self) -> int:if self.empty():return Nonereturn self.que[-1]def empty(self) -> bool:return not self.que

4、有效的括号

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

有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合。
  • 左括号必须以正确的顺序闭合。
  • 注意空字符串可被认为是有效字符串。

思路:这是一个对称匹配问题,由于栈结构的特殊性,非常适合做对称匹配类的题目。通过一次遍历,逐个把左括号加入栈中,如果遇到了匹配的右括号,则把左括号pop出,遍历结束刚好空栈表示这是有效的括号。

class Solution:def isValid(self, s: str) -> bool:stack=[]for item in s:#当遍历左符号时,把对应的右符号加入栈,方便匹配if item=='(':stack.append(')')elif item=='{':stack.append('}')elif item=='[':stack.append(']')#当遍历右符号时,如果不等于栈顶,或者栈是空,表示匹配失败elif not stack or stack[-1]!=item:return False#匹配成功则把对应的符号pop出,继续遍历else:stack.pop()#最后栈空为Truereturn True if not stack else False

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

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

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

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

思路:使用栈来存放,那么栈的目的,就是存放遍历过的元素,当遍历当前的这个元素的时候,去栈里看一下我们是不是遍历过相同数值的相邻元素,一样的话表示重复,pop掉,最后剩下的就是没有相邻重复的了,拼接成字符串就行。

class Solution:def removeDuplicates(self, s: str) -> str:stack = []for item in s:if stack and item == stack[-1]:stack.pop()else:stack.append(item)return ''.join(stack)

6、逆波兰表达式求值

根据 逆波兰表示法,求表达式的值。

有效的运算符包括 + ,  - ,  * ,  / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

说明:

整数除法只保留整数部分。 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6

思路:把元素逐个加入栈中,如果遇到运算符,则把栈最后2个元素拿出来计算,再把结果放进栈中。

class Solution:def evalRPN(self, tokens: List[str]) -> int:stack = []for item in tokens:if item not in {'+','-','*','/'}:stack.append(int(item)) #字符串转数字else:fig2 = int(stack.pop())fig1 = int(stack.pop())if item=='+':stack.append(fig1+fig2)elif item=='-':stack.append(fig1-fig2)elif item=='*':stack.append(fig1*fig2)elif item=='/':stack.append(fig1/fig2)return int(stack.pop())

7、滑动窗口最大值

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

返回滑动窗口中的最大值。

进阶:你能在线性时间复杂度内解决此题吗?

思路:这是使用单调队列的经典题目,难点是如何求一个区间里的最大值呢?

1.暴力方法:遍历一遍的过程中每次从窗口中再找到最大的数值,这样很明显是O(n × k)的算法。

2.使用队列:队列内元素有序,最大值放在出队口,队列内没必要维护窗口内所有元素,只需要维护可能成为窗口内最大值的元素就可以了,所以每次队列加入新元素时,把队列中小于新元素的值都去掉,保证加入的元素永远是队列中的最小值,并且加在队列的右边(末尾),这样队列中永远从左到右都是从大到小的顺序,第一个元素就是最大值。弹出元素的时候只需要判断是否要弹出最大值即可。

class MyQueue:#单调队列,从大到小def __init__(self):self.queue = deque()def pop(self,value):#先判断queue是否为空#如果要弹出的元素=队列出口(最大)元素则弹出if self.queue and value == self.queue[0]:self.queue.popleft()def push(self,value):#先判断queue是否为空#push元素时,把小于要加入的元素都弹出,再把push,保证每次push入的都是队列中的最小值while self.queue and value>self.queue[-1]:self.queue.pop() #把右边最后一个元素弹出self.queue.append(value)def front(self):#队列中的最大值等于队列左边(出口)第一个元素return self.queue[0]class Solution(object):def maxSlidingWindow(self, nums, k):que = MyQueue() #单调队列res = []#先把第一个窗口的元素加入队列for i in range(k):que.push(nums[i])   #每次加入元素时,都会剔除掉队列中小于该元素的值,并且队列从大到小排列res.append(que.front()) #记录第一个窗口的最大值for i in range(len(nums)-k):#窗口每次滑动que.pop(nums[i])     #移除窗口最前面的元素que.push(nums[i+k])  #加入窗口后一个元素res.append(que.front())   #保存结果return res

8、前K个高频元素

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。

示例 1:

  • 输入: nums = [1,1,1,2,2,3], k = 2
  • 输出: [1,2]

示例 2:

  • 输入: nums = [1], k = 1
  • 输出: [1]

提示:

  • 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
  • 你的算法的时间复杂度必须优于 $O(n \log n)$ , n 是数组的大小。
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
  • 你可以按任意顺序返回答案。

思路:

http://www.xdnf.cn/news/6844.html

相关文章:

  • AI知识梳理——RAG、Agent、ReAct、LangChain、LangGraph、MCP、Function Calling、JSON-RPC
  • 电机试验平台:创新科技推动电动机研究发展
  • 多模态学习(三)—— ROPE位置编码:从理论到实践
  • JavaScript入门【1】概述
  • 进阶-数据结构部分:​​​​​​​2、常用排序算法
  • OpenHarmony平台驱动使用 (二),Camera
  • SQL语句执行问题
  • 【AI算法工程师面试指北】ResNet为什么用avgpool结构?
  • Python 基础之函数命名
  • Redis持久化机制详解:保障数据安全的关键策略
  • MySQL表的约束(上)
  • LeetCode 第 45 题“跳跃游戏 II”
  • Spring之Bean的初始化 Bean的生命周期 全站式解析
  • PyTorch实现CrossEntropyLoss示例
  • AIGC在电商行业的应用:革新零售体验
  • 计算机网络(1)——概述
  • Docker入门指南:镜像、容器与仓库的核心概念解析
  • Redis的Hot Key自动发现与处理方案?Redis大Key(Big Key)的优化策略?Redis内存碎片率高的原因及解决方案?
  • STM32 | FreeRTOS 递归信号量
  • C# 深入理解类(静态函数成员)
  • golang中的反射示例
  • 大模型AI原生应用效果测试与评估视频课来啦
  • Python多进程编程执行任务
  • sudo apt update是什么意思呢?
  • (3)python爬虫--Xpath
  • 2022河南CCPC(前四题)
  • pip升级或者安装报错怎么办?
  • 致敬经典 << KR C >> 之打印输入单词水平直方图和以每行一个单词打印输入 (练习1-12和练习1-13)
  • 最小二乘法拟合直线,用线性回归法、梯度下降法实现
  • SLAM定位常用地图对比示例