栈与队列印象不太深刻了,记得之前学的时候应该用C自己实现过栈,队列,不过忘记是用数组还是链表写的了,也忘记怎么写的了…
一刷用python:python栈与队列实现 、python栈与队列内部实现
二刷学C++:C++栈与队列内部实现
LeetCode232-用栈实现队列
题目描述:请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(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(双端队列)来模拟一个栈,只要是标准的栈操作即可
输入:
[“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
题目链接:https://leetcode.cn/problems/implement-queue-using-stacks/
解题思路
- 需要两个栈一个输入栈,一个输出栈
- 在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)
class MyQueue(object):
def __init__(self):
self.s1 = [];
self.s2 = [];
def push(self, x):
"""
:type x: int
:rtype: None
"""
self.s1.append(x);
def pop(self):
# 把s1栈中的所有元素放进s2中
# pop栈s2中第一个元素,即实现先进先出
# 再把s2中的元素放回s1中
# 这样的写法不如随想录中写法,因为其实把s2元素放回s1中是没有必要的
# pop一次下次再pop的话还得再进行s1放入s2,多了一步
"""
:rtype: int
"""
if self.empty(): return None;
while(len(self.s1) != 0):
self.s2.append(self.s1.pop());
res = self.s2.pop();
while(len(self.s2) != 0):
self.s1.append(self.s2.pop());
return res;
def peek(self):
# 这里没有复用pop,以后要注意代码复用
"""
:rtype: int
"""
if self.empty(): return None;
while(len(self.s1) != 0):
self.s2.append(self.s1.pop());
res = self.s2.pop();
self.s1.append(res);
while(len(self.s2) != 0):
self.s1.append(self.s2.pop());
return res;
def empty(self):
"""
:rtype: bool
"""
if len(self.s1) == 0: return True;
else: return False;
注意:
peek()的实现,直接复用了pop(), 要不然,对stOut判空的逻辑又要重写一遍。在工业级别代码开发中,最忌讳的就是 实现一个类似的函数,直接把代码粘过来改一改就完事了。一定要懂得复用,功能相近的函数要抽象出来,不要大量的复制粘贴,很容易出问题!
心得体会
力扣不让调用第三方库,一开始不知道,一直想用lifoqueue,后来发现报错,以后要注意这一点。
栈和队列等数据结构可以用数组(列表)或者链表、双向队列来做,按照栈、队列的规则去做即可。
LeetCode225-用队列实现栈
题目描述:请你仅使用两个队列实现一个后入先出(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
题目链接:https://leetcode.cn/problems/implement-stack-using-queues/
解题思路
- 两个队列实现栈
- 一个队列实现栈
1 两个队列实现栈
- 队列是先进先出的规则,把一个队列中的数据导入另一个队列中,数据的顺序并没有变,并没有变成先进后出的顺序,故一个输入队列,一个输出队列是不可以的
- 用两个队列que1和que2实现队列的功能,que2其实完全就是一个备份的作用,把que1最后面的元素以外的元素都备份到que2,然后弹出最后面的元素,再把其他元素从que2导回que1
# 用list实现,没将que2导回que1,而是que1,que2互作备份
class MyStack(object):
def __init__(self):
self.q1 = [];
self.q2 = [];
def push(self, x):
"""
:type x: int
:rtype: None
"""
self.q1.append(x);
def pop(self):
"""
:rtype: int
"""
if self.empty(): return None;
if len(self.q1) != 0:
while(len(self.q1) > 1):
self.q2.append(self.q1.pop(0));
return self.q1.pop(0);
else:
while(len(self.q2) > 1):
self.q1.append(self.q2.pop(0));
return self.q2.pop(0);
def top(self):
"""
:rtype: int
"""
if self.empty(): return None;
res = self.pop();
self.push(res);
return res;
def empty(self):
"""
:rtype: bool
"""
if len(self.q1) == 0 and len(self.q2) == 0:
return True;
else:
return False;
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会存放数据,所以返回第一个即可
"""
if self.empty():
return None
return self.queue_in[-1]
def empty(self) -> bool:
"""
因为只有in存了数据,只要判断in是不是有数即可
"""
return len(self.queue_in) == 0
2 一个队列实现栈
- 一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时再去弹出元素就是栈的顺序了
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 None
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]
def empty(self) -> bool:
return not self.que
3 心得体会
一开始写的代码直接pop最后一个元素了,不符合先进先出的规律,是不对的
后来写了两个队列实现的,但是一个队列实现的也很好想
注意:随想录里面用的是双向队列,原因是list的pop(0)复杂度是O(n)。但是deque的底层实现我理解为链表,因此在pop头元素的时候,复杂度为O(1)。资料:STL各容器的底层实现及其优缺点
总结
用栈构建队列,队列构建栈并不难,写的时候注意用的数据结构需要严格符合栈、队列的进出规则
要对栈、队列的基本操作非常熟悉,需要会自己实现栈、队列
实现中运用list或者deque即可,要对栈、队列的基本操作非常熟悉,一般list常用的方法是append(),pop();deque常用方法是popleft(),append()
需要充分了解代码中数据结构的底层实现,会分析自己写的代码的复杂度