栈是限制仅在表的一端(表尾)进行操作(插入和删除)的线性表,是一种后进先出(Last in First Out)的数据结构,简称 LIFO。表尾又叫栈顶(Top),允许插入和删除,那么另一端就叫做栈底(Bottom),啥也不能干,只能干等着第一个进栈的过来躺着。栈的插入操作,叫做入栈(push)。存入栈的元素之间没有任何具体的关系,只有到来的时间的先后顺序。栈的删除操作,叫做出栈(pop)。
下面我们讨论一下栈的访问,搜索,插入,删除等操作。
栈的访问,插入与删除(栈顶):
由于后进先出原则,栈的访问,插入与删除偶是对栈顶元素进行的,时间复杂度为O(1)
栈的搜索:
栈的搜索要从栈顶元素开始,一直找到我们要搜索的元素N为止,时间复杂度为O(N)
下面是python中常用的栈的操作:
# create an stack
stack = []
# add element time complexity:O(1)
stack.append(1)
stack.append(2)
stack.append(3)
print(stack) # [1,2,3]
# get the top of stack time complexity:O(1)
stack[-1]
# remove the top of stack time complexity:O(1)
temp = stack.pop
print(temp) # 3
# get size
size = len(stack)
print(size) # 2
# Iterate stack time complexity:O(N)
while len(stack) > 0:
temp = stack.pop()
print(temp)
熟悉了栈的相关操作后我们来看看对应的简单题。力扣20有效的括号:
对于这道题,我们首先定义一个栈用来存放左扩号,在遍历字符串的过程中,遇到左括号就将其存进栈中,若遇到右括号,则将之与栈中的左括号进行匹配,若匹配失败则返回false,重复直到全部遍历完成,若都匹配成功了则返回True。
def isValid(self, s: str):
if len(s) == 0:
return True
stack = [] # 定义一个栈
for c in s:
if c == '(' or c == '[' or c == '{': # 遍历整个字符串,如果是左括号,放到栈里去
stack.append(c)
else: # 如果是右括号,
if len(stack) == 0: # 栈里面为空,就没有对应的左括号返回false
return False
else: # 不为空
temp = stack.pop() # 定义一个临时变量把栈里的左括号拿出来看看匹不匹配
if c == ')'and temp != '(':
return False
if c == ']' and temp != '[':
return False
if c == '}' and temp != '{':
return False
if len(stack) == 0: # 都匹配了,遍历完了,就证明符合,匹配的右括号
return True
else: # 如果栈里还有,证明有左括号没有与之匹配的右括号,返回false
return False
# 时间复杂度:O(N)
# 空间复杂度:O(N)
再来看一下力扣496下一个更大元素:
对于这道题,首先定义一个RES数组来存储下一个更大的元素,定义一个栈存放num2,再对num1进行遍历,依次去对比num1中元素和栈中元素的大小,找出下一个更大元素,返回给RES数组,同时temp用于临时存放栈中元素以便还原。
class Solution:
def nextGreaterElement(self, nums1: List[int], nums2: List[int]):
RES = []
stack = []
for num in nums2:
stack.append(num) # 把num2放进栈里
for i in nums1:
temp = [] # 定义临时变量存储栈出来的元素以便恢复原来的栈
found = False # 定义布尔值决定循环是否继续
max = -1 # 比x大的值初始赋值-1
while(len(stack ) != 0 and found == False):
top = stack.pop() # 依次取出值
if top > i: # 大于num1里的值,先赋值给max
max = top
elif top == i: # 等于num1里的值,证明找到这个元素了(唯一),结束循环
found = True
temp.append(top) # 把取出来的放在temp里面,等一下再放回去恢复栈的原样
RES.append(max)
while(len(temp) != 0):
stack.append(temp.pop()) # 把temp里的值依次放回stack里面
return RES
# time complexity: O(MN) 分别遍历了两个数组
# space complexity: O(N) num2存放在shack里面
队列是限制仅在一端进行插入操作,在另一端进行删除操作的线性表。是一种先进先出(First in First Out)的数据结构,简称 FIFO。允许删除的一端叫做队头,允许插入的一端叫做队尾。队列的插入叫做入队,队列的删除叫做出队。
下面我们讨论一下栈的访问,搜索,插入,删除等操作。
队列的访问与搜索:
由于先进先出原则,队列的访问与搜索都需要遍历队列直到找到要找的元素,时间复杂度为O(N)
队列的插入与删除:
同样由于先进先出原则,队列的插入与删除可以直接在头和尾进行,时间复杂度为O(1)
下面是python中常用的队列的操作:
# create a queue
queue = deque()
# add element time complexity:O(1)
queue.append(1)
queue.append(2)
queue.append(3)
print(queue) # [1,2,3]
# get the head of queue time complexity:O(1)
temp1 = queue[0]
print(temp1) # 1
# remove the head of queue time complexity:O(1)
temp2 = queue.popleft()
print(temp2) # 1
print(queue) # [2,3]
# get size
size = len(queue)
print(size) # 2
# Iterate stack time complexity:O(N)
while len(queue) != 0:
temp = queue.popleft()
print(temp)
熟悉了队列的相关操作后我们来看看对应的简单题。力扣933最近的求和次数:
对于这道题,我们定义一个队列q,把时间t挨个加进来,取t-3000(即保证两次请求在3000毫秒内)的值超过就出列,返回队列长度即可。
class RecentCounter:
def __init__(self):
self.q = deque()
def ping(self, t: int) -> int:
self.q.append(t) # 挨个加入队列
while(self.q[0] < t - 3000): # 保证队列内的请求范围在3000ms内
self.q.popleft() # 把第一个元素删掉即出队
return len(self.q) # 返回队列长度即在0-3000ms内的请求次数
# time complexity:O(N) while循环把t全部添加了一次
# space complexity:O(N) 产生了一个队列