题目描述

✅ 941. 有效的山脉数组

image-20260929105313705

image-20260929105313940

题意分析

判断整个数组是否先严格上升、再严格下降。峰顶必须在内部,上坡和下坡都至少有一段相邻变化,因此数组至少包含三个元素;相邻相等形成的平台不合法。

解法:上坡与下坡两阶段扫描

核心思路

[!blue]

用一个指针 i 从头开始,只要 arr[i] < arr[i+1] 就继续前进。停下时,[0,i] 已经是一段完整的严格上升前缀,i 是唯一需要考虑的峰顶位置:选得更早会把后续上升放进下坡,选得更晚又必须跨过当前这次非上升变化,无法保持上坡。

若 i==0,说明从一开始就没有上坡;若 i==n-1,说明整个数组都在上升、没有下坡。这两种情况都应立即返回 false,从而保证峰顶位于内部。

随后从这个位置继续前进,但条件改为 arr[i] > arr[i+1]。只有走到数组末尾,才说明峰顶之后全部严格下降;若停在中途,就存在平台或再次上升,整个数组不是山脉。

成功时,第一阶段保证至少一次上升,内部峰顶加上第二阶段到达末尾保证至少一次下降,两阶段又覆盖了全部相邻关系,恰好满足定义。反过来,任何合法山脉都会在真实峰顶结束第一阶段,并沿下降段走到末尾,所以不会漏判。

解题步骤

  1. 长度小于三直接返回 false。
  2. 沿严格上升推进指针。
  3. 排除峰顶在首尾的情况。
  4. 沿严格下降继续,判断是否到达末尾。

平台会同时阻止严格上升与严格下降;纯递增或纯递减会被峰顶位置检查排除。判断的是整个数组,找到局部峰顶并不足以返回成功。

代码实现

class Solution {
    public boolean validMountainArray(int[] arr) {
        int n = arr.length;

        if (n < 3) {
            return false;
        }

        int i = 0;

        // 严格上升才前进,遇到相等或下降就停在峰顶候选位。
        while (i + 1 < n && arr[i] < arr[i + 1]) {
            i++;
        }

        // 峰顶不能落在两端,否则缺上坡或缺下坡。
        if (i == 0 || i == n - 1) {
            return false;
        }

        // 从峰顶继续严格下降。
        while (i + 1 < n && arr[i] > arr[i + 1]) {
            i++;
        }

        // 只有恰好走到末尾,才说明中间没有平台或二次上升。
        return i == n - 1;
    }
}
func validMountainArray(arr []int) bool {
    n := len(arr)
    if n < 3 {
        return false
    }

    i := 0
    // 严格上升才前进,遇到相等或下降就停在峰顶候选位。
    for i+1 < n && arr[i] < arr[i+1] {
        i++
    }
    // 峰顶不能落在两端,否则缺上坡或缺下坡。
    if i == 0 || i == n-1 {
        return false
    }
    // 从峰顶继续严格下降。
    for i+1 < n && arr[i] > arr[i+1] {
        i++
    }
    // 只有恰好走到末尾,才说明中间没有平台或二次上升。
    return i == n-1
}

复杂度分析

  • 时间复杂度:$O(n)$,两个阶段合计单向扫描。
  • 空间复杂度:$O(1)$。

关键点总结

[!green]

  • 严格比较自动排除平台。
  • 峰顶不能在两端,保证两种坡都存在。
  • 最后必须走完整个数组,不能只找到局部山峰。

易错点总结

[!yellow]

  • 上升或下降条件不能带等号,否则会把平台当成合法山坡。
  • 只找到一次下降就返回成功,会漏查后面的再次上升或平台。
  • 比较相邻项前先检查 i+1<n,避免在末尾越界。

相似题目

题目 难度 关联与区别
845. 数组中的最长山脉 中等 原题在任意区间找最长山脉,本题要求整个数组都满足先严格升后严格降。
852. 山脉数组的峰顶索引 中等 原题已保证是山脉,只需定位峰,本题必须验证两侧都非空且没有平坡。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2021/43175461
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!