1 滑动窗口
窗口只能往右走,L–或R++
时间复杂度o(1)
和首尾指针类似
1.1 窗口题目
假设一个固定大小为w的窗口,依次划过arr,返回每一次划过状况的最大值
例如,arr=[4,3,5,4,3,3,6,7],w=3
返回[5,5,5,4,6,7]
这里双端队列设定是从大向右,越来越小
这里有一个小知识
from collections import deque
引入双端队列,collections 是python工具集合
deque操作
https://blog.csdn.net/jamfiy/article/details/88188595
from collections import deque
def getMaxWindow(arr,w):
if arr is None or w < 1 or len(arr) < w: # 长度小于窗口
return None
# 双端队列中存放的是位置,arr[位置],队列头部代表数 大->小
qmax = deque()
# 最后结果是多少
res = [0] * (len(arr) - w + 1) # 观察出来的公式,需要建立这么大数组
index = 0
# L...R
# i
# 当前让i -> [i] 进窗口,i就是r
for R in range(len(arr)):
# R位置所代表的值,可以放在比他大的数后,或者空
# 下面是怎么进窗口逻辑
# 不为空且尾巴值小于等于我,弹到比我大的数后面或弹到空,就停
l = len(qmax)
while l != 0 and arr[qmax[l - 1]] <= arr[R]:
qmax.pop() # 一直从尾巴弹出
l = len(qmax)
qmax.append(R)
# 数字进来了
# 如果窗口没有形成w的长度之前,不弹出数字 来到R位置,R-w就是越界数字
if qmax[0] == R - w: # 如果双端队列头部位置等于R-w
qmax.popleft() # 是越界下标,就从头部弹一个位置
# 以上窗口更新就做完了
if R >= w - 1: # 窗口没有形成w时不收集答案,当达成w时收集长度
res[index] = arr[qmax[0]] # 达成长度就收集答案,收集头部值
index += 1
return res
arr = [4,3,5,4,3,3,6,7]
w = 3
print(getMaxWindow(arr,w))
1.2 题目2
给定一个整形数组arr,和一个整数num
某个arr中的子数组sub,如果想达标,必须满足:
sub中最大值 - sub中最小值 <= num
返回arr中达标子数组的数量
时间复杂度:O(N)
from collections import deque
def getNum(arr,num):
if all == None or len(arr) == 0:
return 0
qmin = deque()
qmax = deque()
L = 0
R = 0
# [L...R) [0,0) [1,1) 代表窗口没有数字
# [0,1) 代表有一个数
res = 0 # 一共的数量
# L是开头位置,尝试每一个开头
while L < len(arr):
# 如果此时窗口开头是L,下面的while工作:R向右扩到违规为止
while R < len(arr): # R是最后一个达标位置的再下一个
while len(qmin) != 0 and arr[qmin[-1]] >= arr[R]: # R位置数进来时,怎么更新
qmin.pop() # 一直从尾巴弹出
qmin.append(R)
# 最大值更新方式
while len(qmax) != 0 and arr[qmax[-1]] <= arr[R]:
qmax.pop() # 一直从尾巴弹出
qmax.append(R)
# 监测是否达标
if arr[qmax[0]] - arr[qmin[0]] > num:
break
R += 1
# 从上面循环跳出时,R来到了第一个不达标位置,0....R-1 是达标的
# R是最后一个达标位置的再下一个,第一个违规的位置
res += R - L # 累加
# 最小值的更新结构和最大值的更新结构查看是否过期
if qmin[0] == L:
qmin.popleft()
if qmax[0] == L:
qmax.popleft()
L += 1
return res
arr = [3,4,5,6]
num = 3
print(getNum(arr,num))
2 单调栈
2.1解决:离一个数最近的数,左边是什么,右边是什么
from collections import deque
# 为什么返回二维数组呢
# 比如 arr[0 ,2,1,4,5]
# 0 1 2 3 4
# [
# 0 : [-1,1] 第0行,-1代表左边离它近小于它的数是没有
# 1 : [-1,2] 第1个位置的数
# ]
def getNearLess(arr):
res = [[0] * 2 for _ in range(len(arr))]
# 单调栈放的是位置,同样值的东西,位置压在一起
# 位置代表值,是从小到大的
# 准备一个数组 [3,2,3,5,3,1] 对应位置为 0 1 2 3 4 5
stack = []
for i in range(len(arr)): # i->arr[i] 进栈
# 从底到顶,从小到大 栈顶元素中,列表的第一个位置的值 是否大于当前值 这里是arr[0] = 3 > arr[1] = 2 把整个list弹出
while len(stack) != 0 and arr[stack[len(stack) - 1][0]] > arr[i]:
popIs = stack.pop() # 弹出栈顶列表
# 取位于下面位置的列表中,最晚加入的那个 -1 取列表中最后一个
leftLessIndex = -1 if len(stack) == 0 else stack[-1][-1]
for popi in popIs:
res[popi][0] = leftLessIndex # 左边近且最小
res[popi][1] = i # 右边近且最小
# 相等的,比你小的
if len(stack) != 0 and arr[stack[0]] == arr[i]:
stack[-1].append(i) # 下标压到列表内
else:
list = [] # 生成一个列表
list.append(i) # 0放进去 i==0 [1]代表2
stack.append(list) # 列表放入栈中
while len(stack) != 0: # 栈内还有东西
popIs = stack.pop()
# 取位于下面位置的列表中,最晚加入的那个
leftLessIndex = -1 if len(stack) == 0 else stack[-1][-1]
for popi in popIs:
res[popi][0] = leftLessIndex # 左边近且最小
res[popi][1] = -1 # 右边开始清算,所以都是-1
return res
2.2 求子数组最大值是多少
给定一个只包含正整数的数组arr,arr中任何一个子数组sub一定都可以算出(sub累加和)*(sub中的最小值)是什么,
那么所有子数组中,这个值最大是多少?
这也可以用单调栈,范围变大指标变大或变小 —>单调性