栈与队列理论基础
首先先总结一下最基础的内容:栈(stack)遵循后进先出(LIFO),队列(queue)遵循先进先出(FIFO)。
然后总结一下python的collection库中栈与队列实现的底层容器。在collection中,栈和队列都是由deque(双向链表)所实现的,而且不同于C++,collection中是无法指定实现其功能的底层容器的。只有在了解了栈与队列实现的底层容器与机制后我们才可以更好的去解决算法题目。
232. 用栈实现队列
学习视频:栈的基本操作! | LeetCode:232.用栈实现队列_哔哩哔哩_bilibili
学习文档:代码随想录 (programmercarl.com)
学习时间:15:35-16:23
记录时间:16:24-16:45
状态:已听懂|可单独复写代码(定义class还不熟练)|暂不需复习
1. 看到问题后的初始想法
第一眼看到这题目就感觉仅仅使用一个栈是无论如何都实现不了队列的,可惜由于思维就局限并没有想到可以使用两个栈解决问题,下意识就认为只可以使用一个栈了,以后得改正。
2. 看完随想录后的迭代想法
十分巧妙的是,本题可以使用两个栈来解决问题。由于我们使用的语言是python,在不掉collection包的情况下没有stack这个数据结构,所以本题我们使用数组来替代stack。尽管我们使用数组来代替栈,但我们还是只用栈的函数来解决问题,不会使用队列独有的函数来解决问题。我们首先定义一个列表叫stack_in,再定义一列表叫stack_out。stack_in是用来存储push进来的元素的,stack_out是用来记录pop出去的元素的。具体来讲,当队列使用push时,我们把元素append进stack_in中。当我们要pop时,我们先判断stack_out有没有元素,如果stack_out有元素,那我们就pop stack_out中的元素,如果没有元素,我们就把stack_in中的所有元素pop出,并依次装入stack_out中(完成stack中元素的反向排列,这样stack_out的最后一个元素就是我们在队列中要得到的元素。(注意:stack_in的元素进入stack_out后所有元素都反向排列了。
代码如下(要注意python class的定义与使用):
class MyQueue(object):
def __init__(self):
self.stack_in = []
self.stack_out = []
def push(self, x):
"""
:type x: int
:rtype: None
"""
self.stack_in.append(x)
def pop(self):
"""
:rtype: int
"""
if not self.stack_out:
while self.stack_in:
self.stack_out.append(self.stack_in.pop())
ans = self.stack_out.pop()
return ans
def peek(self):
"""
:rtype: int
"""
ans = self.pop() # 一定要懂得复用函数
self.stack_out.append(ans)
return ans
def empty(self):
"""
:rtype: bool
"""
if not self.stack_in and not self.stack_out:
return True
else:
return False
# 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()
225. 用队列实现栈
学习视频:队列的基本操作! | LeetCode:225. 用队列实现栈_哔哩哔哩_bilibili
学习文档:代码随想录 (programmercarl.com)
学习时间:16:45-17:29
记录时间:17:29-
状态:已听懂|可单独复写代|暂不需复习
1. 看到问题后的初始想法
说实话,第一次看到这题我感觉有点多此一举。因为队列和栈本身就不是底层容器,而使用队列去实现栈给人一种叠积木而不是从底层开始构建的感觉。不过这既然是一道算法题、一道面试题,其更多的是考验我们的思路以及对于栈和队列的掌控能力,本身就不具备多少实用价值。我偷偷扫了眼题解,看到用双队列和单队列都可以,我就先脑内构思了下双队列,发现这个双队列和上一题的双栈还不一样,双栈是通过改变数据输出方向来达到目的,而双队列的话无法改变数据方向,纠结了一会,无果,遂思考单队列。由于单队列就那几个操作,因此我想到了一个很“蠢”但可行的方法,即为了使队列输出最后一个元素,我只要把最后一个元素之前的元素全部按顺序pop出来,再按先后顺序push进去,最后再pop最后一个元素(此时最后一个元素已经在队列第一个了)即可。而只要完成相对来说最复杂的pop操作,其他的操作也就很简单了。top函数只要在pop函数的基础上把pop出来的数字再塞回队列最后一个即可。
但对于python来说,如果使用数组来充当队列,时间复杂度是很高的。每次数组pop出第一个元素,复杂度就是o(n),然而我们要循环size-1次以达到最后一个元素!因此复杂度就是o(n2),和stack的pop o(1)来说简直没法比。
class MyStack(object):
def __init__(self):
self.queue = []
self.size = 0
def push(self, x):
"""
:type x: int
:rtype: None
"""
self.queue.append(x)
self.size += 1
def pop(self):
"""
:rtype: int
"""
for i in range(self.size-1):
temp = self.queue.pop(0)
self.queue.append(temp)
ans = self.queue.pop(0)
self.size -= 1
return ans
def top(self):
"""
:rtype: int
"""
ans = self.pop()
self.queue.append(ans)
self.size += 1
return ans
def empty(self):
"""
:rtype: bool
"""
if self.size == 0:
return True
else:
return False
# 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()
2. 看完随想录后的迭代想法
单队列法居然和我刚开始想的一摸一样!(出息了哈哈哈哈)这里补充一下双队列,这里双队列的使用方法也很“质朴”,和单队列的区别就是pop的时候把最后一个元素之前的所有元素都存在了第二个队列里面,等把最后一个元素返回后再重新移回来。。。