今天给大家带来一篇面试高频算法题之栈&队列的详细解析,全文包含9道大厂笔试面试算法真题,一举拿下栈和队列这个知识点,让算法不在成为进入大厂的绊脚石。
栈是一种先进后出的数据结构。这里有一个非常典型的例子,就是堆叠盘子。我们在放盘子的时候,只能从下往上一个一个的放;在取的时候,只能从上往下一个一个取,不能从中间随意取出。
栈是一种操作受限的线性表,只允许在一端处理数据。主要包括两种操作,即入栈和出栈,也就是在栈顶插入一个数据和从栈顶删除一个数据。
栈既可以用数组实现,也可以用链表来实现。用数组实现的栈,我们叫作顺序栈,用链表实现的栈,我们叫作链式栈。
队列是一种先进先出的数据结构。你可以把它想象成排队买票,先来的先买,后来的人只能站末尾,不允许插队。
队列跟栈一样,也是一种操作受限的线性表数据结构。主要包括两个操作,即出队和入队,也就是从队首取一个元素和在队尾插入一个元素。
队列可以用数组来实现,也可以用链表来实现。用数组实现的队列叫作顺序队列,用链表实现的队列叫作链式队列。
剑指 Offer 09. 用两个栈实现队列
用两个栈来实现一个队列,完成 n 次在队列尾部插入整数 (push) 和在队列头部删除整数 (pop) 的功能。队列中的元素为 int 类型。保证操作合法,即保证pop操作时队列内已有元素。
示例:
输入:["PSH1","PSH2","POP","POP"]
返回值:1,2
说明:
"PSH1":代表将1插入队列尾部 "PSH2":代表将2插入队列尾部 "POP":代表删除一个元素,先进先出 => 返回1 "POP":代表删除一个元素,先进先出 => 返回2
首先,我们需要知道队列和栈的区别。
为了使用两个栈实现队列的先进先出的特性,我们需要用一个栈来反转元素的入队顺序。
入队操作Push:
因为栈是后进先出的,而队列是先进先出的,所以要想使用栈来实现队列的先进先出功能,我们需要把新入栈的元素放入栈底。为了实现这个操作,我们需要先把栈S1中的元素移动到S2,接着再把新来的元素压入S2,然后再把S2中的所有元素再弹出,压入到S1。这样就实现了把新入栈的元素放入栈底的功能。
def push(self, node):
# write code here
while self.stack1:
self.stack2.append(self.stack1.pop())
self.stack2.append(node)
while self.stack2:
self.stack1.append(self.stack2.pop())
出队操作Pop:
我们直接从S1弹出就可以了,因为经过反转后,S1中的栈顶元素就是最先入栈的元素,也就是队首元素。
def pop(self):
if self.stack1:
return self.stack1.pop()
我们来看完整的代码实现。
class Solution:
def __init__(self):
self.stack1 = []
self.stack2 = []
def push(self, node):
# write code here
while self.stack1:
self.stack2.append(self.stack1.pop())
self.stack2.append(node)
while self.stack2:
self.stack1.append(self.stack2.pop())
def pop(self):
if self.stack1:
return self.stack1.pop()
我们可以看到入队操作的时间复杂度是O(n),空间复杂度也是O(n)。出队时间复杂度是O(1),空间复杂度也是O(1)。
在上面的算法中,不知道你有没有发现,每次在push一个新元素时,我们都需要把S1中的元素移动到S2中,然后再从S2移回到S1中。这显然是冗余的。其实,我们在入队时只需要插入到S1中即可。而出队的时候,由于第一个元素被压在了栈S1的底部,要想实现队列的先进先出功能,我们就需要把S1的元素进行反转。我们可以把栈S1的元素Pop出去,然后压入S2。这样就把S1的栈底元素放在了栈S2的栈顶,我们直接从S2将它弹出即可。一旦 S2 变空了,我们只需把 S1 中的元素再一次转移到 S2 就可以了。
下面我们来看一下代码实现。
class Solution:
def __init__(self):
self.stack1 = []
self.stack2 = []
def push(self, node):
# write code here
self.stack1.append(node)
def pop(self):
if not self.stack2:
while self.stack1:
self.stack2.append(self.stack1.pop())
return self.stack2.pop()
LeetCode 20. 有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符满足的条件是:
示例:
输入:s = "()[]{}"
输出:true
这个问题我们可以借助 “栈” 这种数据结构来解决。在遍历字符串的过程中,当我们遇见一个左括号时,我们就入栈,当我们遇到一个右括号时,我们就取出栈顶元素去判断他们是否是同类型的。如果不是的话,那就代表字符串s不是有效串,我们直接返回False。如果是,接着去遍历,直到遍历结束为止。当遍历完字符串s后,如果栈为空,就代表字符串是有效的。这里需要注意一点,为了加快判断左、右括号是否是同类型的,我们引入哈希表存储每一种括号。哈希表的键为右括号,值为相同类型的左括号。
下面我们来看一下代码实现。
def isValid(s):
#如果字符串不是偶数,直接返回false
#因为字符只包含括号,所以只有偶数时才有可能匹配上
if len(s) % 2 == 1:
return False
dict = {
")": "(",
"]": "[&