栈的基本操作
在Python中,可以使用列表来实现栈的主要操作。栈是一种遵循后进先出(LIFO)原则的数据结构。以下是栈的主要操作:
- 入栈(Push):将元素添加到栈的顶部。
- 出栈(Pop):从栈的顶部移除并返回元素。
- 栈顶(Top):返回栈顶部的元素,但不对栈进行修改。
- 栈空(IsEmpty):检查栈是否为空,如果栈中没有元素则返回 True,否则返回 False。
- 栈大小(Size):返回栈中元素的个数。
class Stack:
def __init__(self):
self.stack = []
def push(self, item):
self.stack.append(item)
def pop(self):
if not self.is_empty():
return self.stack.pop()
else:
print("Stack is empty.")
def top(self):
if not self.is_empty():
return self.stack[-1]
else:
print("Stack is empty.")
def is_empty(self):
return len(self.stack) == 0
def size(self):
return len(self.stack)
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print(stack.top()) # 输出 3
stack.pop()
print(stack.size()) # 输出 2
print(stack.is_empty()) # 输出 False
用栈实现队列
使用栈实现队列的下列操作:
push(x) -- 将一个元素放入队列的尾部。
pop() -- 从队列首部移除元素。
peek() -- 返回队列首部的元素。
empty() -- 返回队列是否为空。
栈:后进先出 队列:先进先出
所以,要想用栈实现队列,就必须用两个栈:输入栈跟输出栈
在push数据的时候,只要数据放进输入栈就好,但在pop的时候,操作就复杂一些,输出栈如果为空,就把进栈数据全部导入进来(注意是全部导入),再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。
最后如何判断队列为空呢?如果进栈和出栈都为空的话,说明模拟的队列为空了。
class MyQueue:
def __init__(self):
"""
in主要负责push,out主要负责pop
"""
self.stack_in = []
self.stack_out = []
def push(self, x: int) -> None:
"""
有新元素进来,就往in里面push
"""
self.stack_in.append(x)
def pop(self) -> int:
"""
Removes the element from in front of queue and returns that element.
"""
if self.empty():
return None
if self.stack_out:
return self.stack_out.pop()
else:
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:
"""
Get the front element.
"""
ans = self.pop()
self.stack_out.append(ans)
return ans
def empty(self) -> bool:
"""
只要in或者out有元素,说明队列不为空
"""
return not (self.stack_in or self.stack_out)
用队列实现栈
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(
push
、top
、pop
和empty
)。实现
MyStack
类:
void push(int x)
将元素 x 压入栈顶。int pop()
移除并返回栈顶元素。int top()
返回栈顶元素。boolean empty()
如果栈是空的,返回true
;否则,返回false
。
操作跟上面很像,同样也需要用两个队列。
用两个队列que1和que2实现队列的功能,que2其实完全就是一个备份的作用,把que1最后面的元素以外的元素都备份到que2,然后弹出最后面的元素,再把其他元素从que2导回que1。
Python中队列的主要操作包括:
创建队列:使用Queue模块创建队列,可以选择先进先出(FIFO)或后进先出(LIFO)类型的队列。
入队:使用put()方法将元素加入队列中。
出队:使用get()方法从队列中取出元素。
判断队列是否为空:使用empty()方法判断队列是否为空。
获取队列长度:使用qsize()方法获取队列中元素的个数。
清空队列:使用queue.clear()方法清空队列中所有元素。
阻塞/非阻塞操作:可以设置队列为阻塞或非阻塞模式,当队列为空时阻塞线程等待元素入队或者不阻塞直接返回None。可以使用Queue模块中的put(block=True)和get(block=True)方法来设置阻塞模式。
设置队列大小:可以设置队列的最大容量,一旦超过最大容量,队列将会阻塞。可以使用Queue模块中的Queue(maxsize=10)方法来设置队列最大容量为10。
deque
(双端队列) 是一个支持在两端进行加入和删除操作的序列数据结构。它可以高效地实现栈、队列、双向队列等数据结构。Python 的
collections
模块提供了deque
类型,我们可以使用它来创建双端队列。使用deque
类型时,可以在左边或右边添加或删除元素,而且这些操作的时间复杂度都是 O(1) 级别的,因此它非常适合需要频繁插入和删除元素的场景。下面是一个简单的例子,演示建并使用一个双端队列:
from collections import deque
class MyStack:
def __init__(self):
"""
Python普通的Queue或SimpleQueue没有类似于peek的功能
也无法用索引访问,在实现top的时候较为困难。
用list可以,但是在使用pop(0)的时候时间复杂度为O(n)
因此这里使用双向队列,我们保证只执行popleft()和append(),因为deque可以用索引访问,可以实现和peek相似的功能
in - 存所有数据
out - 仅在pop的时候会用到
"""
self.queue_in = deque()
self.queue_out = deque()
def push(self, x: int) -> None:
"""
直接append即可
"""
self.queue_in.append(x)
def pop(self) -> int:
"""
1. 首先确认不空
2. 因为队列的特殊性,FIFO,所以我们只有在pop()的时候才会使用queue_out
3. 先把queue_in中的所有元素(除了最后一个),依次出列放进queue_out
4. 交换in和out,此时out里只有一个元素
5. 把out中的pop出来,即是原队列的最后一个
tip:这不能像栈实现队列一样,因为另一个queue也是FIFO,如果执行pop()它不能像
stack一样从另一个pop(),所以干脆in只用来存数据,pop()的时候两个进行交换
"""
if self.empty():
return None
for i in range(len(self.queue_in) - 1):
self.queue_out.append(self.queue_in.popleft())
self.queue_in, self.queue_out = self.queue_out, self.queue_in # 交换in和out,这也是为啥in只用来存
return self.queue_out.popleft()
def top(self) -> int:
"""
写法一:
1. 首先确认不空
2. 我们仅有in会存放数据,所以返回第一个即可(这里实际上用到了栈)
写法二:
1. 首先确认不空
2. 因为队列的特殊性,FIFO,所以我们只有在pop()的时候才会使用queue_out
3. 先把queue_in中的所有元素(除了最后一个),依次出列放进queue_out
4. 交换in和out,此时out里只有一个元素
5. 把out中的pop出来,即是原队列的最后一个,并使用temp变量暂存
6. 把temp追加到queue_in的末尾
"""
# 写法一:
# if self.empty():
# return None
# return self.queue_in[-1] # 这里实际上用到了栈,因为直接获取了queue_in的末尾元素
# 写法二:
if self.empty():
return None
for i in range(len(self.queue_in) - 1):
self.queue_out.append(self.queue_in.popleft())
self.queue_in, self.queue_out = self.queue_out, self.queue_in
temp = self.queue_out.popleft()
self.queue_in.append(temp)
return temp
def empty(self) -> bool:
"""
因为只有in存了数据,只要判断in是不是有数即可
"""
return len(self.queue_in) == 0
有效括号
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 注意空字符串可被认为是有效字符串。
这道题是栈的经典应用,本质上就是使符号一个个进栈,如果进栈的括号与前一个匹配,这一组括号就出栈→如果最终栈为空,则改组括号为有效括号
- 如果进栈的括号与前一个匹配:这一操作实现起来很复杂,我们可以换一种思路,在进栈时,将该括号变成与他匹配的括号(如‘【’ 变为 ‘】’ ),这样只需在进栈时看二者是否相等即可。
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
else:
stack.pop()
return True if not stack else False
删除字符串中的所有相邻重复项
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
- 输入:"abbaca"
- 输出:"ca"
- 解释:例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
本题是“有效括号”的简易版本,思路参考有效括号即可。
class Solution:
def removeDuplicates(self, s: str) -> str:
res = list()
for item in s:
if res and res[-1] == item:
res.pop()
else:
res.append(item)
return "".join(res) # 字符串拼接