五、栈与队列(1)
- 理论基础
- [232.用栈实现队列 ](https://leetcode.cn/problems/implement-queue-using-stacks/description/)
- [225. 用队列实现栈 ](https://leetcode.cn/problems/implement-stack-using-queues/description/)
- [20. 有效的括号 ](https://leetcode.cn/problems/valid-parentheses/description/)
- [ 1047. 删除字符串中的所有相邻重复项](https://leetcode.cn/problems/remove-all-adjacent-duplicates-in-string/description/)
理论基础
在Python中,与栈(stack)[]和队列(queue)= deque()相关的问题可以通过一些内置的数据结构和模块来解答。
1. Python中的stack是容器么?
是的,栈(stack)是一种常用的数据容器。在Python中,栈通常使用列表(list)来实现,虽然Python没有专门的栈数据结构,但列表提供的append()和pop()方法可以非常方便地实现栈的功能。
2. 我们使用的stack是属于哪个版本的STL?
Python中没有C++标准模板库(STL)的概念,因为Python的标准库已经包含了许多内置的容器类型和模块来实现类似的功能。栈在Python中可以使用内置的list实现,也可以使用collections模块中的deque来实现。
3. 我们使用的stack是如何实现的?
在Python中,可以通过list或collections.deque来实现栈:
-
使用list实现栈:
stack = [] stack.append(1) # 压栈 stack.append(2) # 压栈 item = stack.pop() # 出栈,item = 2
-
使用collections.deque实现栈:
from collections import deque stack = deque() stack.append(1) # 压栈 stack.append(2) # 压栈 item = stack.pop() # 出栈,item = 2
deque(双端队列)在性能上比list更优,因为它是为高效的头部和尾部操作而设计的。
4. stack提供迭代器来遍历stack空间么?
是的,Python中的list和deque都提供迭代器来遍历其元素。例如:
-
使用list迭代:
stack = [1, 2, 3, 4] for item in stack: print(item)
-
使用deque迭代:
from collections import deque stack = deque([1, 2, 3, 4]) for item in stack: print(item)
这两种方法都可以方便地遍历栈中的所有元素。
在Python中,collections.deque
(双端队列)是一个可以高效地在两端添加和删除元素的容器。要实现先进先出(FIFO)队列,可以使用deque
的以下方法:
append()
在队列尾部添加元素pop()
尾部弹出并返回元素popleft()
从队列头部删除并返回元素appendleft()
队列头部添加元素
deque 的其他方法
collections.deque
还提供了其他有用的方法,如:
extend(iterable)
:在队列尾部添加多个元素extendleft(iterable)
:在队列头部添加多个元素(注意,这些元素会以倒序添加到队列中)remove(value)
:移除队列中第一次出现的value
rotate(n)
:旋转队列,正数n
将元素右移,负数n
将元素左移
这些方法使得deque
非常灵活,可以用于多种不同的数据处理需求。
代码示例
from collections import deque
# 创建一个deque并添加一些元素
queue = deque(['a', 'b', 'c'])
# 在尾部添加元素
queue.append('d')
print(queue) # 输出: deque(['a', 'b', 'c', 'd'])
# 在头部添加元素
queue.appendleft('z')
print(queue) # 输出: deque(['z', 'a', 'b', 'c', 'd'])
# 从尾部删除元素
print(queue.pop()) # 输出: d
print(queue) # 输出: deque(['z', 'a', 'b', 'c'])
# 从头部删除元素
print(queue.popleft()) # 输出: z
print(queue) # 输出: deque(['a', 'b', 'c'])
# 扩展队列
queue.extend(['e', 'f'])
print(queue) # 输出: deque(['a', 'b', 'c', 'e', 'f'])
# 头部扩展队列
queue.extendleft(['y', 'x']) # 倒序插入
print(queue) # 输出: deque(['x', 'y', 'a', 'b', 'c', 'e', 'f'])
# 旋转队列
queue.rotate(2)
print(queue) # 输出: deque(['e', 'f', 'x', 'y', 'a', 'b', 'c'])
queue.rotate(-3)
print(queue) # 输出: deque(['y', 'a', 'b', 'c', 'e', 'f', 'x'])
232.用栈实现队列
借助另一个出栈,栈实现队列的出队操作效率低下:栈底元素(对应队首元素)无法直接删除,需要将上方所有元素出栈。
可以设计栈 A 用于加入队尾操作,栈 B 用于将元素倒序,从而实现删除队首元素。
法一,老老实实每次倒出来再倒回去.
class MyQueue:
def __init__(self):
self.a, self.b = [], []
def push(self, x: int) -> None:
self.a.append(x)
def pop(self) -> int:
# a not null
while self.a:
self.b.append(self.a.pop())
ans = self.b.pop()
while self.b:
self.a.append(self.b.pop())
return ans
def peek(self) -> int:
# a not null
while self.a:
self.b.append(self.a.pop())
ans = self.b[-1]
while self.b:
self.a.append(self.b.pop())
return ans
def empty(self) -> bool:
return not self.a
# 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()
法二,始终保持B是倒序A是正序B是倒序的,反正要peek完B里面的才轮到后面新A加进来的。而且也没说要返回一个完整的队列。
如果B不为空,返回B的最后一个,也就是原来最先进A的;如果B为空,这时有两个情况,A也为空,返回-1,A不为空,说明(可能一开局或者已经pop完了),就把A倒序倒到B中。`刚开始不理解,万一后面A又push新的了怎么办?新的还在A里面,如果要pop或者peek,反正先把B 里面的搞完,如果还要继续peek,此时B空了,又回到A不空,B空的条件,把A倒进B。
类内部函数互相调用也得self,不可以直接从B里面pop,因为不知道B是否空return self.B.pop()虽然能直接返回,但是错错错
代码复用,减少错的可能
class MyQueue:
def __init__(self):
self.A, self.B = [], []
def push(self, x: int) -> None:
self.A.append(x)
def pop(self) -> int:
# peek返回的元素肯定是从B里面来的
# 删除也在B里面删 直接pop即可
# 要返回删除的值
val = self.peek() # 类内部调用
self.B.pop()
return val
# 不可以直接从B里面pop,因为不知道B是否空
# return self.B.pop()
def peek(self) -> int:
if self.B:
return self.B[-1]
elif not self.A:
return -1
else:
while self.A:
self.B.append(self.A.pop())
return self.B[-1]
def empty(self) -> bool:
return not self.A and not self.B # A B都是空(false)才true,
# 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()
时间复杂度: push和empty为O(1), pop和peek为O(n)
空间复杂度: O(n)
225. 用队列实现栈
只用一个队列实现,此时队列只能popleft(),拿出来再放到末尾,用len()控制循环
class MyStack:
def __init__(self):
self.a = deque()
def push(self, x: int) -> None:
self.a.append(x)
def pop(self) -> int:
for _ in range(len(self.a)-1):
self.a.append(self.a.popleft())
return self.a.popleft()
def top(self) -> int:
for _ in range(len(self.a)-1):
self.a.append(self.a.popleft())
ans = self.a.popleft()
self.a.append(ans)
return ans
def empty(self) -> bool:
return not self.a
# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()
20. 有效的括号
利用先进后出(更早期待,更晚遇到)
先想有哪些错误类型1 左括号多了 2 右括号多了 3 括号不匹配(即与预判的不相等)
class Solution:
def isValid(self, s: str) -> bool:
stack = []
# 不考虑空
for c in s:
if c == '(':
stack.append(')')
elif c == '{':
stack.append('}')
elif c == '[':
stack.append(']')
elif stack and c == stack.pop() :
continue
else:
return False # 匹配不上的,或者右边多,stack已空
return not stack
时间复杂度: O(n)
空间复杂度: O(n)
1047. 删除字符串中的所有相邻重复项
这种重复or对称的都可以用更早预判放入栈,更晚遇到相比较的栈的数据结构处理
class Solution:
def removeDuplicates(self, s: str) -> str:
stack = []
for c in s:
if stack:
temp = stack.pop()
if c == temp:
s = s.replace(c+c, "")
else:
stack.append(temp)
stack.append(c)
else:
stack.append(c)
return s
想知道栈的最后一个直接[-1]即可
类似消消乐,也可以把所有都放入栈,遇到相同的消除,最后"".join(res)
字符串拼接一下