SWE · Coding

ByteDance OA 局部峰:两侧向外严格递减,如何线性检查?

把localArea定义为向外相邻比较的半径,预计算递增/递减长度,处理端点、平台及半径为零。

题目重点:把localArea定义为向外相邻比较的半径,预计算递增/递减长度,处理端点、平台及半径为零。

题意与假设

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

  • 训练按从候选向外的连续相邻值比较,不任意跳过元素选子序列;返回下标。
  • localArea为非负整数,某侧无元素时该侧条件真;半径0时全部位置符合。
  • 严格下降不允许相等。若正式返回峰值或使用非连续subsequence,需另改输出或判定合同。

解题路线

  1. 相邻严格峰值求和变体只看内部节点 i=1..n−2,要求值大于左右相邻,并累加峰值而非下标。与允许端点的 localArea 合同分开:示例[2,3,1,5,4]内部峰3/5,和8。代码另提供strict_interior_peak_sum,不用带端点的函数直接求和。
  2. 直接对每个i检查半径R范围的相邻关系需O(nR)。因为各窗口大量重叠,可以预计算截至i左侧连续递增的边数L[i],及从i右侧连续递减的边数D[i]。
  3. 向左走是值依次下降,等价于沿数组从左向右到i严格上升。若a[i−1]<a[i]则L[i]=L[i−1]+1,否则0;左侧需要min(R,i)条合法边。
  4. 从右往左算D:若a[i]>a[i+1]则D[i]=D[i+1]+1,否则0。右侧需要min(R,n−1−i)条边。这两个长度条件同时满足即输出i。
  5. 条件足够性来自连续run:至少所需边数说明窗口内每条边均严格正确;必要性是窗口内若有任何坏边,连续长度会在那条边归零,无法达到所需长度。
  6. 端点不能一律排除,因为合同明确不足半径取现有元素。单元素数组两侧均空,对任意非负半径都合格;严格平台中的相等边使对应run断开。
  7. 文字中的subsequences容易让人以为可以跳点,正式面试要用[a,b,c]的小例确认。本实现采用连续片段合同并单列这个边界;没有凭“局部最大”名称替换成只比较相邻两点。

手动走一遍

[1,3,2,1]、R=2:下标1向左只有1个值,3>1;向右3>2>1,合格。下标3右侧空,但往左1>2不成立,不合格。R=0则[0,1,2,3]都符合。[2,2]、R=1无峰。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

"""Return peak indices. From a candidate outward, all available comparisons
within radius local_area must decrease strictly. Endpoints may be peaks.
"""


def local_peak_indices(values, local_area):
    if not isinstance(local_area, int) or isinstance(local_area, bool) or local_area < 0:
        raise ValueError("nonnegative integer radius required")
    n = len(values)
    increasing = [0] * n
    decreasing = [0] * n
    for i in range(1, n):
        if values[i - 1] < values[i]:
            increasing[i] = increasing[i - 1] + 1
    for i in range(n - 2, -1, -1):
        if values[i] > values[i + 1]:
            decreasing[i] = decreasing[i + 1] + 1
    return [i for i in range(n)
            if increasing[i] >= min(local_area, i)
            and decreasing[i] >= min(local_area, n - 1 - i)]


def strict_interior_peak_sum(values):
    """Immediate neighbors only, endpoints excluded; sum values, not indices."""
    return sum(values[i] for i in range(1, len(values)-1)
               if values[i] > values[i-1] and values[i] > values[i+1])

复杂度

O(n)时间,左右run数组与结果O(n)空间。若只要计数,可不保存输出;还可用坏边前缀和或流式滑动检查降低工作空间,但需保持边界合同。

易错点与追问

容易失分的边界

  • 向左严格下降写成原数组向右下降。
  • 无条件排除端点。
  • 把相等当严格。
  • 只看邻居忽略localArea。

继续深挖

  • 若返回值不是位置如何处理重复?
  • R极大时怎样避免越界?
  • 可否用坏边前缀和实现?
  • 流式延迟R个元素怎样输出?
✓已复制微信号!