
本文详解为何无需数组全局有序,仅凭局部单调性(先严格递增后严格递减)即可用二分查找精准定位峰值索引,并剖析其收敛逻辑与边界处理原理。
本文详解为何无需数组全局有序,仅凭局部单调性(先严格递增后严格递减)即可用二分查找精准定位峰值索引,并剖析其收敛逻辑与边界处理原理。
该算法看似违背二分查找“仅适用于有序数组”的常识,实则巧妙利用了输入数组的特殊结构——山峰数组(Mountain Array):存在唯一索引 i,使得 arr[0] arr[i+1] > ... > arr[n-1]。整个数组虽非全局有序,但具有明确的单峰性和方向可判性,这正是二分策略成立的关键前提。
核心思想不是比较 mid 与目标值,而是通过 arr[mid] 与其右侧邻居 arr[mid + 1] 的大小关系,判断峰值位于左半段还是右半段:
- 若
arr[mid] > arr[mid + 1]:说明当前处于“下坡段”,峰值必在mid或其左侧(因为左侧仍可能持续上升直至顶点),故收缩右边界:h = mid; - 若
arr[mid] :说明当前处于“上坡段”,峰值必在 <code>mid + 1或其右侧(左侧所有元素均小于arr[mid+1],不可能为峰值),故收缩左边界:l = mid + 1。
注意:这里不比较 arr[mid] 与 arr[mid - 1],是因为 mid 可能为 0(越界),而 mid + 1 始终在 [0, len(arr)-2] 范围内(因循环条件 l 保证 <code>mid 不会取到末尾索引)。
以下为带注释的参考实现:
from typing import List
def peakIndexInMountainArray(arr: List[int]) -> int:
l, h = 0, len(arr) - 1
while l arr[mid + 1]:
h = mid # 峰值在 [l, mid] 区间内(含 mid)
else:
l = mid + 1 # 峰值在 [mid+1, h] 区间内
return l # 最终 l == h,即峰值索引
以 arr = [0,10,5,2] 为例执行过程:
- 初始:
l=0, h=3→mid=1,arr[1]=10 > arr[2]=5→h=1 - 此时
l==h==1,循环结束,返回1,正确。
关键注意事项:
- 该算法强依赖山峰数组定义(严格递增→严格递减,无相等相邻元素),若存在平台(如
[1,2,2,3,2])或多个峰,结果不可靠; - 时间复杂度稳定为 O(log n),空间复杂度 O(1),显著优于线性扫描;
- 循环终止时
l与h必然重合,且指向唯一峰值——这是由每次收缩都保留包含峰值的子区间所保证的数学不变性(invariant)。
综上,二分查找的本质是“基于可判定的方向性进行区间剪枝”,而非狭义的“在有序数组中查值”。只要问题具备单峰性与局部可导向性,它便能大放异彩。










