数据结构-栈和队列

1. 栈的定义

栈(stack)是限定仅在表尾进行插入或者删除的线性表。对于栈来说,表尾端称为栈顶(top),表头端称为栈底(bottom)。因为栈限定在表尾进行插入或者删除,所以栈又被称为后进先出(LIFO)的线性表。
stack

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.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_)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值