目录
理论基础
了解一下 栈与队列的内部实现机制,文中是以C++为例讲解的。
文章讲解:代码随想录
232.用栈实现队列
大家可以先看视频,了解一下模拟的过程,然后写代码会轻松很多。
题目链接/文章讲解/视频讲解:https://programmercarl.com/0232.%E7%94%A8%E6%A0%88%E5%AE%9E%E7%8E%B0%E9%98%9F%E5%88%97.html
题目
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
实现 MyQueue 类:
void push(int x)将元素 x 推到队列的末尾int pop()从队列的开头移除并返回元素int peek()返回队列开头的元素boolean empty()如果队列为空,返回true;否则,返回false
说明:
- 你 只能 使用标准的栈操作 —— 也就是只有
push to top,peek/pop from top,size, 和is empty操作是合法的。 - 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
示例 1:
输入: ["MyQueue", "push", "push", "peek", "pop", "empty"] [[], [1], [2], [], [], []] 输出: [null, null, null, 1, 1, false] 解释: MyQueue myQueue = new MyQueue(); myQueue.push(1); // queue is: [1] myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue) myQueue.peek(); // return 1 myQueue.pop(); // return 1, queue is [2] myQueue.empty(); // return false
提示:
1 <= x <= 9- 最多调用
100次push、pop、peek和empty - 假设所有操作都是有效的 (例如,一个空的队列不会调用
pop或者peek操作)
进阶:
- 你能否实现每个操作均摊时间复杂度为
O(1)的队列?换句话说,执行n个操作的总时间复杂度为O(n),即使其中一个操作可能花费较长时间。
代码
class MyQueue:
def __init__(self):
"""
in负责push,out负责pop
"""
self.stack_in=[]
self.stack_out=[]
def push(self, x: int) -> None:
self.stack_in.append(x)
def pop(self) -> int:
"""用两个栈模拟队列的pop操作,先进先出"""
if self.stack_out:
return self.stack_out.pop()#删除并返回最右边的元素
else:#如果out栈为空,则将in栈的元素全部弹出到out栈
for i in range(len(self.stack_in)):
#将in栈的元素全部弹出到out栈
self.stack_out.append(self.stack_in.pop())
return self.stack_out.pop()## 然后从输出栈弹出最上面那个元素(也就是原来最先进入队列的)
def peek(self) -> int:
#返回列表最前面的元素,但不删除它
ans=self.pop()#pop操作会删除元素,所以先pop出来
self.stack_out.append(ans)
return ans
def empty(self) -> bool:
return not self.stack_in and not self.stack_out
#如果两个栈都为空,则队列为空
✅ 问题:
在
peek()方法中,为什么是用self.pop(),而不是直接self.stack_out.pop()?
✅ 答:
因为
self.pop()是一个封装好的“队列出队”操作,它已经处理了:
如果
stack_out是空的,就从stack_in中倒元素过来否则就直接
stack_out.pop()
所以我们用 self.pop() 是为了统一处理所有情况,确保行为正确。
225. 用队列实现栈
可能大家惯性思维,以为还要两个队列来模拟栈,其实只用一个队列就可以模拟栈了。
建议大家掌握一个队列的方法,更简单一些,可以先看视频讲解
题目链接/文章讲解/视频讲解:代码随想录
题目
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
void push(int x)将元素 x 压入栈顶。int pop()移除并返回栈顶元素。int top()返回栈顶元素。boolean empty()如果栈是空的,返回true;否则,返回false。
注意:
- 你只能使用队列的标准操作 —— 也就是
push to back、peek/pop from front、size和is empty这些操作。 - 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
示例:
输入: ["MyStack", "push", "push", "top", "pop", "empty"] [[], [1], [2], [], [], []] 输出: [null, null, null, 2, 2, false] 解释: MyStack myStack = new MyStack(); myStack.push(1); myStack.push(2); myStack.top(); // 返回 2 myStack.pop(); // 返回 2 myStack.empty(); // 返回 False
提示:
1 <= x <= 9- 最多调用
100次push、pop、top和empty - 每次调用
pop和top都保证栈不为空
进阶:你能否仅用一个队列来实现栈。
代码
from collections import deque
class MyStack:
def __init__(self):
# 初始化一个队列,用于模拟栈
self.que = deque()
def push(self, x: int) -> None:
# 将元素 x 添加到队列末尾(正常入队)
self.que.append(x)
def pop(self) -> int:
# 模拟栈的出栈操作:返回并删除最后一个压入的元素
if self.empty():
return None # 栈为空时返回 None
# 核心思想:将前 n-1 个元素重新入队,留下最后一个元素在队首
for 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 None
# return self.que[-1]
# 写法二(更符合“通过 pop 实现”的结构)
if self.empty():
return None
# 和 pop 一样,先把前 n-1 个元素移动到队尾
for i in range(len(self.que) - 1):
self.que.append(self.que.popleft())
# 此时最后一个元素在队首(就是栈顶)
temp = self.que.popleft()
# 把它重新加入队尾,恢复结构
self.que.append(temp)
# 返回这个“栈顶元素”,但不删除
return temp
def empty(self) -> bool:
# 判断栈是否为空
return not self.que # 队列为空就是栈为空
20. 有效的括号
讲完了栈实现队列,队列实现栈,接下来就是栈的经典应用了。
大家先自己思考一下 有哪些不匹配的场景,在看视频 我讲的都有哪些场景,落实到代码其实就容易很多了。
题目链接/文章讲解/视频讲解:代码随想录
题目
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入:s = "()"
输出:true
示例 2:
输入:s = "()[]{}"
输出:true
示例 3:
输入:s = "(]"
输出:false
示例 4:
输入:s = "([])"
输出:true
提示:
1 <= s.length <= 104s仅由括号'()[]{}'组成
使用栈的思路
| 步骤 | 当前字符 item | 操作说明 | 栈内容 |
|---|---|---|---|
| 1 | ( | 是左括号,压入 ) | [')'] |
| 2 | { | 是左括号,压入 } | [')', '}'] |
| 3 | [ | 是左括号,压入 ] | [')', '}', ']'] |
| 4 | ] | 匹配成功,弹出 ] | [')', '}'] |
| 5 | } | 匹配成功,弹出 } | [')'] |
| 6 | ) | 匹配成功,弹出 ) | [](栈空) |
| ✅ 结果 | 所有字符成功匹配,栈为空 | ✅ 有效括号字符串 |
| 步骤 | 当前字符 item | 操作说明 | 栈内容 |
|---|---|---|---|
| 1 | ( | 是左括号,压入 ) | [')'] |
| 2 | { | 是左括号,压入 } | [')', '}'] |
| 3 | [ | 是左括号,压入 ] | [')', '}', ']'] |
| 4 | } | ⚠️ 不匹配!期望 ],却遇到 } → 返回 False | [')', '}', ']'] |
| ❌ 结果 | 直接返回 False,括号顺序错误(交叉) | ❌ 无效括号字符串 |
代码
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('}')
# 如果是右括号:
else:
# 如果栈为空,说明没有对应的左括号,直接返回 False
# 或者栈顶不是当前右括号(括号不匹配),也返回 False
if not stack or stack[-1] != item:
return False
# 如果匹配,弹出栈顶括号(匹配成功)
else:
stack.pop()
# 如果最后栈是空的,说明所有括号都成功匹配,返回 True
# 否则说明还有多余的左括号没被匹配,返回 False
return True if not stack else False
测试
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('}')
# 如果是右括号:
else:
# 如果栈为空,说明没有对应的左括号,直接返回 False
# 或者栈顶不是当前右括号(括号不匹配),也返回 False
if not stack or stack[-1] != item:
return False
# 如果匹配,弹出栈顶括号(匹配成功)
else:
stack.pop()
# 如果最后栈是空的,说明所有括号都成功匹配,返回 True
# 否则说明还有多余的左括号没被匹配,返回 False
return True if not stack else False
def test_isValid():
solution = Solution()
test_cases = [
("()", True),
("()[]{}", True),
("(]", False),
("([)]", False),
("{[]}", True),
("", True), # 空字符串视为有效
("{", False), # 单个左括号
("]", False), # 单个右括号
("((()))", True),
("((())", False), # 少一个右括号
("({[})", False), # 括号交叉
("(((((((((())))))))))", True), # 多层嵌套
]
print("开始测试 isValid()...")
for i, (s, expected) in enumerate(test_cases):
result = solution.isValid(s)
status = "✅ PASS" if result == expected else f"❌ FAIL (Got {result})"
print(f"Test {i + 1}: isValid('{s}') → Expected: {expected} | {status}")
test_isValid()
1047. 删除字符串中的所有相邻重复项
栈的经典应用。
要知道栈为什么适合做这种类似于爱消除的操作,因为栈帮助我们记录了 遍历数组当前元素时候,前一个元素是什么。
题目链接/文章讲解/视频讲解:代码随想录
题目
给出由小写字母组成的字符串 s,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 s 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
输入:"abbaca" 输出:"ca" 解释: 例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
提示:
1 <= s.length <= 105s仅由小写英文字母组成。
思想
遍历字符串,元素入栈,遍历到重复就出栈
用栈来存放,那么栈的目的,就是存放遍历过的元素,当遍历当前的这个元素的时候,去栈里看一下我们是不是遍历过相同数值的相邻元素。
代码
class Solution:
def removeDuplicates(self, s: str) -> str:
# 使用栈结构(用列表模拟)来记录最终保留的字符
res = []
# 遍历输入字符串中的每个字符
for item in s:
# 如果栈不为空,并且当前字符和栈顶字符相同
if res and res[-1] == item:
res.pop() # 弹出栈顶(消除重复的一对字符)
else:
res.append(item) # 否则把当前字符压入栈中
# 最后把栈里的字符拼接成字符串返回
return "".join(res)
#测试
def test_removeDuplicates():
solution = Solution()
test_cases = [
("abbaca", "ca"),
("azxxzy", "ay"),
("aababaab", "ba"),
("", ""), # 空串
("a", "a"), # 单个字符
("aa", ""), # 一对重复字符
("abba", ""), # 两组嵌套重复
("abccba", ""), # 多层嵌套消除
("abcdef", "abcdef"), # 无任何重复
]
print("开始测试 removeDuplicates()...\n")
for i, (s, expected) in enumerate(test_cases):
result = solution.removeDuplicates(s)
status = "✅ PASS" if result == expected else f"❌ FAIL (Got '{result}')"
print(f"Test {i + 1}: removeDuplicates('{s}') → Expected: '{expected}' | {status}")
test_removeDuplicates()
422

被折叠的 条评论
为什么被折叠?



