SWE · Coding

Meta 最长区间Max−Min不超Limit:两条单调队列

求最大值减最小值不超过阈值的最长连续子数组,区别最短超阈值题。

题目重点:求最大值减最小值不超过阈值的最长连续子数组,区别最短超阈值题。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 训练max−min≤limit,返回最长长度,空0、负limit没有非空合法段。输入值可负,重复值合法。
  • 这与最短max−min≥K题输出和不等号都不同,不共用收缩停止规则。

解题路线

  1. lows维护值非递减索引、highs值非递增索引,队首是窗口最小/最大。入新值从尾淘汰被新值支配的候选,保存新索引。
  2. 队首差>limit时收缩left,若过期索引在队首则pop,再left+1。合法后以right结尾窗口取最长长度。
  3. 新右端只可能增加范围,删左端只可能减小范围,所以存在单调left推进。之前被排除的更早左端不会因后续加元素重新合法。
  4. 重复值可保留或用非严格比较淘汰旧值,只要按索引过期逻辑一致。本例保留同值,不能按值等于出窗元素就删任意候选。

手动走一遍

[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停止条件搬来会反向缩窗。
  • 队列只存值无法判断同值不同索引是否过期。

继续深挖

  • 如果要返回所有最长窗口,输出可能线性,如何记录而不复制每段?
  • 频繁任意位置修改不再是滑动过程,需什么范围查询结构?
✓已复制微信号!