题目重点:求最大值减最小值不超过阈值的最长连续子数组,区别最短超阈值题。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练max−min≤limit,返回最长长度,空0、负limit没有非空合法段。输入值可负,重复值合法。
- 这与最短max−min≥K题输出和不等号都不同,不共用收缩停止规则。
解题路线
- lows维护值非递减索引、highs值非递增索引,队首是窗口最小/最大。入新值从尾淘汰被新值支配的候选,保存新索引。
- 队首差>limit时收缩left,若过期索引在队首则pop,再left+1。合法后以right结尾窗口取最长长度。
- 新右端只可能增加范围,删左端只可能减小范围,所以存在单调left推进。之前被排除的更早左端不会因后续加元素重新合法。
- 重复值可保留或用非严格比较淘汰旧值,只要按索引过期逻辑一致。本例保留同值,不能按值等于出窗元素就删任意候选。
手动走一遍
[8,2,4,7]、limit4最长[2,4]或[4,7]长度2;[10,1,2,4,7,2]、limit5最长[2,4,7,2]长度4。limit0时只允许同值段。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
from collections import deque
def longest_bounded_range(values,limit):
"""Longest nonempty contiguous segment with max-min<=limit; empty ->0."""
if limit<0:return 0
lows=deque();highs=deque();left=best=0
for right,value in enumerate(values):
while lows and values[lows[-1]]>value:lows.pop()
while highs and values[highs[-1]]<value:highs.pop()
lows.append(right);highs.append(right)
while values[highs[0]]-values[lows[0]]>limit:
if lows[0]==left:lows.popleft()
if highs[0]==left:highs.popleft()
left+=1
best=max(best,right-left+1)
return best
复杂度
O(N)时间,每索引各入出两deque一次;最坏O(N)队列空间。用每窗口sort或min/max扫描会退化,不因双指针就自动线性。
易错点与追问
容易失分的边界
- 把另一题>=K停止条件搬来会反向缩窗。
- 队列只存值无法判断同值不同索引是否过期。
继续深挖
- 如果要返回所有最长窗口,输出可能线性,如何记录而不复制每段?
- 频繁任意位置修改不再是滑动过程,需什么范围查询结构?
