目录
[一、用 栈 实现 队列](#一、用 栈 实现 队列)
[二、面试题 03.04. 化栈为队](#二、面试题 03.04. 化栈为队)
[三、用 队列 实现 栈](#三、用 队列 实现 栈)
[四、225. 用队列实现栈](#四、225. 用队列实现栈)
于是我停下脚步,痛定思痛
------ 25.3.4
一、用 栈 实现 队列
1.概念
所谓用栈实现队列,就是要求实现一个队列,只不过这个队列的底层容器,不是顺序表,也不是链表,而是栈。要满足这个需求,必须用到两个栈,一个叫 入队栈 s1,一个叫 出队栈 s2
2.入队操作
将元素压住 入队栈 s1中
3.出队操作
首先,判断 出队栈 s2 是否为空,如果为空,则将 入队栈 s1 中的元素全部弹出并依次压入 出队栈 s2
然后,弹出 出队栈 s2 的栈顶元素并返回
4.获取队首元素
首先,判断 出队栈 s2 是否为空,如果为空,则将 入队栈 s1 中的元素全部弹出并依次压入 出队栈 s2 (和出队操作一致)
然后,获取 出队栈 s2 栈顶元素并返回
5.队列判空
如果 入队栈 s1 和 出队栈 s2 都为空,则返回true,否则返回false
二、面试题 03.04. 化栈为队
实现一个MyQueue类,该类用两个栈来实现一个队列。
示例:
MyQueue queue = new MyQueue(); queue.push(1); queue.push(2); queue.peek(); // 返回 1 queue.pop(); // 返回 1 queue.empty(); // 返回 false
说明:
- 你只能使用标准的栈操作 -- 也就是只有
push to top
,peek/pop from top
,size
和is empty
操作是合法的。- 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
- 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。
思路与算法
双栈结构设计 :Stack1
作为输入栈 ,负责接收新元素(push
操作)Stack2
作为输出栈 ,负责弹出队列头部元素(pop
/peek
操作)通过栈的反转特性 实现队列的先进先出(FIFO)逻辑
关键操作逻辑:
入队(push
) :直接压入Stack1
,时间复杂度为 O(1)
出队(pop
) :若Stack2
为空,将Stack1
所有元素依次弹出并压入Stack2
(实现顺序反转),弹出Stack2
栈顶元素(即队列头部元素),均摊时间复杂度 O(1)
查看队首(peek
) :逻辑与pop
类似,但仅查看不弹出元素。
判空(empty
):同时检查两个栈是否为空
python
class Stack:
def __init__(self):
self.data = []
# 入栈操作 直接相当于列表的append操作
def push(self, val):
self.data.append(val)
# 出栈操作 直接相当于列表的pop操作
def pop(self):
if self.empty():
return "Stach is empty"
return self.data.pop()
# 获取栈顶元素 直接相当于列表的索引[-1]操作
def top(self):
if self.empty():
return "Stach is empty"
return self.data[-1]
def size(self):
return len(self.data)
def empty(self):
return len(self.data) == 0
class MyQueue:
def __init__(self):
"""
Initialize your data structure here.
"""
self.Stack1 = Stack()
self.Stack2 = Stack()
def push(self, x: int) -> None:
"""
Push element x to the back of queue.
"""
self.Stack1.push(x)
def pop(self) -> int:
"""
Removes the element from in front of queue and returns that element.
"""
if self.Stack2.empty():
while not self.Stack1.empty():
self.Stack2.push(self.Stack1.pop())
return self.Stack2.pop()
def peek(self) -> int:
"""
Get the front element.
"""
if self.Stack2.empty():
while not self.Stack1.empty():
self.Stack2.push(self.Stack1.pop())
return self.Stack2.top()
def empty(self) -> bool:
"""
Returns whether the queue is empty.
"""
return self.Stack1.empty() and self.Stack2.empty()
# 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()

三、用 队列 实现 栈
1.概念
所谓用队列实现栈,就是要求实现一个栈,只不过这个栈的底层容器,不是顺序表,也不是链表,而是队列。要满足这个需求,必须用到两个队列,一个叫 主队列 q1,一个叫 辅助队列 q2
**注意:**辅助队列每次操作完毕一定会变成空的。
2.入栈操作
将元素在 主队列 q1 中入队
3.出栈操作
第1步: 如果 主队列 q1 没有元素,那么这个操作非法
第2步: 判断 主队列 q1 的元素个数是否大于 1,如果大于 1,则将 主队列 q1 的元素全部按照顺序在 辅助队列 q2 入队
第3步: 获取 主队列 q1 的队首元素,保存起来用于最后返回;并且将 主队列 q1 执行出队操作;
第4步: 将 辅助队列 q2 的元素全部按照顺序在 主队列 q1 入队
第5步: 返回第3步中 保存起来的 主队列 q1 的队首元素
4.获取栈顶元素
第1步: 如果 主队列 q1 没有元素,那么这个操作非法。
第2步: 判断 主队列 q1 的元素个数是否大于 1,如果大于 1,则将 主队列 q1 的元素全部按照顺序在 辅助队列 q2 入队
第3步: 获取 主队列 q1 的队首元素,保存起来用于最后返回;并且将 主队列 q1 执行出队操作,并且将这个元素 在辅助队列 q2 入队
第4步: 将 辅助队列 q2 的元素全部按照顺序在 主队列 q1 入队
第5步: 返回第3步中 保存起来的 主队列 q1 的队首元素
5.栈判空
由于 辅助队列 q2 每次操作完毕必然会变成空的,所以只需要判断 主队列 q1 是否会空,如果 主队列 q1 为空,则返回 true;否则,返回 false。
四、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
都保证栈不为空
思路与算法
双队列策略:
主队列(Queue1):存储栈的当前元素。
辅助队列(Queue2):在出栈操作时临时存储元素。
核心操作分析:
入栈(push) :直接将元素压入 主队列 Queue1
(时间复杂度O(1))
出栈(pop) :将 主队列 Queue1
的前n-1个元素转移到 辅助队列 Queue2
。弹出主队列 Queue1
的最后一个元素作为栈顶。将 辅助队列 Queue2
的元素转移回 主队列 Queue1
(时间复杂度O(n))。
查看栈顶(top):类似出栈操作,但需将最后一个元素重新压回队列(时间复杂度O(n))。
设计特点:
模拟LIFO :通过队列的FIFO特性逆向实现栈的先进后出。
均摊时间复杂度:单次出栈/查看栈顶操作最坏O(n),但均摊后仍可视为O(1)
python
class Queue:
def __init__(self):
self.data = []
# head 和 tail 分别表示队头和队尾的索引,是一个左闭右开的区间
self.head = 0
self.tail = 0
def push(self, val):
self.data.append(val)
self.tail += 1
def pop(self):
if self.is_empty():
raise Exception("Queue is empty")
val = self.data[self.head]
self.head += 1
return val
def front(self):
if self.is_empty():
raise Exception("Queue is empty")
return self.data[self.head]
def size(self):
return self.tail - self.head
def is_empty(self):
return self.size() == 0
def test():
q = Queue()
q.push(1)
q.push(2)
q.push(3)
q.push(4)
while not q.is_empty():
print(f'Front element: {q.front()}')
print(f'Popped element: {q.pop()}')
print(f'Size: {q.size()}')
if __name__ == '__main__':
test()
class MyStack:
def __init__(self):
self.Queue1 = Queue()
self.Queue2 = Queue()
def push(self, x: int) -> None:
self.Queue1.push(x)
def pop(self) -> int:
while self.Queue1.size() > 1:
self.Queue2.push(self.Queue1.pop())
ret = self.Queue1.pop()
while self.Queue2.size() > 0:
self.Queue1.push(self.Queue2.pop())
return ret
def top(self) -> int:
while self.Queue1.size() > 1:
self.Queue2.push(self.Queue1.pop())
ret = self.Queue1.pop()
self.Queue2.push(ret)
while self.Queue2.size() > 0:
self.Queue1.push(self.Queue2.pop())
return ret
def empty(self) -> bool:
return self.Queue1.is_empty() and self.Queue2.is_empty()
# 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()
