1. 栈的定义
栈(stack)是限定仅在表尾进行插入或者删除的线性表。对于栈来说,表尾端称为栈顶(top),表头端称为栈底(bottom)。因为栈限定在表尾进行插入或者删除,所以栈又被称为后进先出(LIFO)的线性表。
1.1 P4387 验证栈序列
思路:
首先将入栈序列的第i个元素压到 辅助栈(temp)中
判断“temp”中的栈顶元素是否和出栈序列中的第idx个元素一致(栈为空就不用判断了):
如果一致则弹出栈顶元素,idx++;
如果不一致则继续压入入栈序列中的第 i+1个元素。
继续判断栈顶元素是否和出栈序列中的第 idx 个元素一致,直到 i 到达入栈序列的末尾。
最后如果“stack”为空就输出Yes,否则输出No
def checkPoped(stack,out,k):
temp=[]
cnt=0
for i in range(k):
temp.append(stack[i])
while temp and temp[-1]==out[cnt]:
temp.pop()
cnt+=1
if temp==[]:
return True
else:
return False
n=int(input().strip())
for i in range(n):
k=int(input().strip())
stack=input().strip().split()
out=input().strip().split()
if checkPoped(stack,out,k):
print('Yes')
else:
print('No')
1.2 括号匹配问题
def check(s):
dic = {')': '(', ']': '['}
st = []
for c in s:
if c in '([':
st.append(c)
else:
if st and dic[c] == st[-1]:
st.pop()
else:
return False
if st == []:
return True
else:
return False
相关题目:P1241括号序列 P1944 最长括号匹配
1.3 AcWing3302. 表达式求值
def calculate(num,ops):
b = num.pop()
a = num.pop()
op = ops.pop()
if op =='+':num.append(a+b)
elif op == '-':num.append(a-b)
elif op == '*':num.append(a*b)
else:num.append(a//b)
def getAns(s):
dic= {'+':1,'-':1,'*':2,'/':2}
ops = []
num = []
op = '+-*/'
for c in s:
if c == "(":
ops.append(c)
elif c == ")":
while ops[-1] != '(':calculate(num,ops)
ops.pop()
elif c.isdigit():
num.append(int(c))
else:
while ops != []and ops[-1] != '(' and dic[c] <= dic[ops[-1]]:
calculate(num,ops)
ops.append(c)
while ops :
calculate(num,ops)
print(num[0])
1.2 单调栈
单调栈中存放的数据是有序的,所以单调栈也分为单调递增栈和单调递减栈
1. 单调递增栈:单调递增栈就是从栈底到栈顶数据是从大到小
2. 单调递减栈:单调递减栈就是从栈底到栈顶数据是从小到大
什么时候使用单调栈:
-
给定一个序列,求序列中的每一个数左边/右边第一个比他小/比他大的数;
-
给定一个序列,求序列中的每一个数左边/右边第一个比他小/比他大的数在什么地方;
1.2.1 P2947 [USACO09MAR]Look Up S
#输入
n = int(input())
H = []
for _ in range(n):
H.append(int(input()))
#单调递减栈
st = []
#存放对应的仰望对象
res = [0] * n
for i in range(n):
#如果st的栈底元素小于入栈元素,出栈
while st and H[st[-1]] < H[i]:
#对于这个让它出栈的元素就是他仰望的对象
res[st.pop()] = i + 1
#如果st为空或者栈底元素大于等于入栈元素,入栈
st.append(i)
#输出
for ans in res:
print(ans)
1.2.2 P1823 [COI2007] Patrik 音乐会的等待
n = int(input())
st = []
H = []
for i in range(n):
H.append(int(input()))
# 如果一个人身后存在一个比他高的人,那么他将不会再被看见,
# 因此,可以可根据身高维护一个单调栈,
# 从左向右依次扫描,扫描到 A 时,我们希望栈里保存的是 A 之前,能够和 A 相互看到的人
ans = 0
for i in range(n):
p = [H[i],1]
while st and st[-1][0]<=p[0]: # 弹掉<=p[0]
ans+=st[-1][1]
if st[-1][0] == p[0]: # 因为弹出了与p[0]相等的元素,所以下次更新答案时要在加上相等的元素数量
p[1]+=st[-1][1]
st.pop()
if st: # 栈中还有比p[0]大的元素
ans+=1
st.append(p)
print(ans)
1.2.3 P2866 [USACO06NOV]Bad Hair Day S
n = int(input())
st = []
ans = 0
for i in range(n):
h = int(input())
while st and st[-1]<=h:
st.pop()
ans+=len(st) # 当前栈中剩下的牛都可以看见这头牛
st.append(h)
print(ans)
2. 队列
队列(Queue): 是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除。其操作特性为 先进先出(First In First Out,FIFO),并且只允许在队尾进,队头出。
2.1 单调队列
队列中的元素始终保持着单增或者单减的特性。
在每次加入或者删除元素时都保持序列里的元素有序,即队首元素始终是最小值或者最大值
单调队列不仅可以队头出列,也能队尾出列
2.1.1 P2032 扫描
from collections import deque
sq = deque()
#输入
n, m = map(int, input().split())
ls = list(map(int, input().split()))
for i in range(n):
#如果长度 > m时,弹出队头元素
if sq and i-sq[0] >= m:
sq.popleft()
#队尾元素小于等于入队元素时,弹出队尾元素
while sq and ls[sq[-1]] <= ls[i]:
sq.pop()
#队列为空或者队尾元素大于入队元素时,入队
sq.append(i)
#输出最大值
if i >= m - 1:
print(ls[sq[0]])
2.1.2 P2251 质量检测
n,m = map(int,input().split())
nums = list(map(int,input().split()))
from collections import deque
que = deque()
# 维护一个单调递增队列 队头为最小的元素
for i in range(n):
while que and i-que[0]>=m:
que.popleft()
while que and nums[que[-1]]>=nums[i]:
que.pop()
que.append(i)
if i>=m-1:
print(nums[que[0]])
2.1.3 P1886 滑动窗口 /【模板】单调队列
n,m = map(int,input().split())
nums = list(map(int,input().split()))
from collections import deque
que = deque()
maxs_ = []
mins_ = []
for i in range(n):
while que and i-que[0]>=m:
que.popleft()
while que and nums[que[-1]]>=nums[i]:
que.pop()
que.append(i)
if i>=m-1:
mins_.append(nums[que[0]])
print(*mins_)
que.clear()
for i in range(n):
while que and i-que[0]>=m:
que.popleft()
while que and nums[que[-1]]<=nums[i]:
que.pop()
que.append(i)
if i>=m-1:
maxs_.append(nums[que[0]])
print(*maxs_)