题目描述

✅ 852. 山脉数组的峰顶索引

image-20260928235834600

题意分析

数组已经保证是山脉形状:先严格递增到某个位置,再严格递减。峰顶唯一,且不在两端,要求返回这个峰顶的下标,而不是峰顶的数值。

不需要验证输入是否为山脉,也不用逐个找最大值。峰顶之前的相邻关系一直是上升,峰顶之后一直是下降,可以根据中点所在的坡向判断峰顶在哪一侧,在对数时间内缩小范围。

解法:二分查找上升坡结束位置

核心思路

[!blue]

用闭区间 [left, right] 保存峰顶可能的位置,初始覆盖整个数组。每轮只比较 arr[mid] 与右邻 arr[mid + 1],判断当前位置是否仍在上坡。

如果 arr[mid] < arr[mid + 1],说明从中点向右还在严格上升,峰顶一定尚未到达。由于山脉只有一个峰,中点及其左侧都不可能是峰顶,令 left = mid + 1。

否则相邻值处于下降关系,中点已经到达峰顶或位于峰顶右侧,峰顶只可能在中点及其左边。因此令 right = mid,必须保留中点,因为下降的第一对元素恰好从峰顶出发。

两个分支都保留唯一峰顶,同时缩小候选区间。循环条件 left < right 配合向下取整中点,保证 mid < right,因此 mid + 1 不会超过当前右边界,访问右邻始终安全。

当两端相遇时,只剩一个候选位置,直接返回它。二分利用的是坡向从上升变为下降的单调性,不要求整个数组从小到大排列。

解题步骤

  1. 初始化 left = 0、right = n - 1。
  2. 当 left < right 时,计算向下取整的中点 mid。
  3. 若中点到右邻仍上升,令 left = mid + 1。
  4. 否则令 right = mid,保留可能就是峰顶的中点。
  5. 区间收敛后返回 left。

代码实现

class Solution {
    public int peakIndexInMountainArray(int[] arr) {
        int left = 0;
        int right = arr.length - 1;

        while (left < right) {
            // 左端小于右端且中点向下取整,右邻始终在数组内
            int mid = left + (right - left) / 2;

            if (arr[mid] < arr[mid + 1]) {
                left = mid + 1;
            } else {
                // 下降时中点仍可能就是峰顶,必须保留
                right = mid;
            }
        }

        return left;
    }
}
func peakIndexInMountainArray(arr []int) int {
    left, right := 0, len(arr)-1

    for left < right {
        // 左端小于右端且中点向下取整,右邻始终在数组内
        mid := left + (right-left)/2
        if arr[mid] < arr[mid+1] {
            left = mid + 1
        } else {
            // 下降时中点仍可能就是峰顶,必须保留
            right = mid
        }
    }
    return left
}

复杂度分析

  • 时间复杂度:$O(\log n)$,候选区间每轮折半。
  • 空间复杂度:$O(1)$,仅二分下标。

关键点总结

[!green]

  • 使用坡度决定方向,不要求全数组升序。
  • 中点安全性来自循环条件与取整方式共同约束。

易错点总结

[!yellow]

  • 上升时令左端等于中点,两候选时可能停滞。
  • 下降时丢掉中点,可能丢掉峰顶。
  • 返回峰顶值而非下标。

相似题目

题目 难度 关联与区别
162. 寻找峰值 中等 同样沿局部上升方向二分,原题可能多个峰,本题保证单一山脉峰顶。
1095. 山脉数组中查找目标值 困难 寻找山脉峰顶是子步骤,再分别在升序段和降序段二分目标值。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2025/79436570
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!