二分查找(山脉数组的峰顶索引)(5)

https://blog.csdn.net/2601_95366422/article/details/158770218

上节课链接

一.题目

852. 山脉数组的峰顶索引 - 力扣(LeetCode)

二.思考讲解

第一步:查找二分性

观察题目描述,数组先递增到一个峰值元素 ,然后递减。这种"先升后降"的单峰特性意味着:在峰值左侧,元素满足 arr[i] < arr[i+1](上升趋势);在峰值右侧,元素满足 arr[i] > arr[i+1](下降趋势)。因此,如果我们取任意一个位置 mid,比较它和它右边的元素 arr[mid+1],就可以判断当前位于峰值的哪一侧:

  • 如果 arr[mid] < arr[mid+1],说明 mid 处于上升段 ,峰值一定在 mid 的右边。

  • 如果 arr[mid] > arr[mid+1],说明 mid 处于下降段 或正好是峰值,峰值在 mid 的左边或就是 mid 本身。 这种"一侧上升一侧下降"的规律,使得我们可以通过比较相邻元素来不断缩小搜索范围,因此数组具备二分性,可以采用二分查找求解。

第二步:采用左端点还是右端点

我们要找的是峰值元素 ,即第一个满足 arr[i] > arr[i+1] 的位置 i(因为一旦进入下降段,第一个下降点就是峰顶)。这是一个典型的寻找第一个满足某条件的位置 问题,因此应该采用左端点二分查找 。用现实中的山来比喻:站在山脚往上看,最先看到的山顶就是第一个出现的最高点,后面即使有更高的山也被遮挡,所以我们要找的是最左边的峰值 。采用左端点模板,中点取向下取整,缩进规则为:

  • arr[mid] < arr[mid+1] 时,说明 mid 还在上升段,峰值在右边,因此 left = mid + 1(舍弃左半区间)。

  • arr[mid] > arr[mid+1] 时,说明 mid 在下降段或就是峰值,峰值在左边或就是 mid,因此 right = mid(保留 mid 继续搜索)。 循环结束时,left 指向的就是第一个下降点,即峰顶索引。

三.代码演示

cpp 复制代码
class Solution {
public:
    int peakIndexInMountainArray(vector<int>& arr) 
    {
        int left = 0;
        int right = arr.size() - 1;
        while(left < right)
        {
            int mid = left + (right - left)/2;
            if(arr[mid] < arr[mid + 1])
            {
                left = mid + 1;
            }
            else
                right = mid;
        }
        return left;
    }
};

四.代码讲解

一、循环条件与中点计算

采用 while (left < right) 作为循环条件,而不是 left <= right。这是因为当左右指针相遇时,区间内只剩一个元素,此时无需继续二分 ,可以直接在循环外返回该元素。这种写法可以避免死循环,并符合左端点模板的常规写法。

中点计算采用向下取整 的公式,即 mid = left + (right - left) / 2。当区间长度为偶数时,中点会偏向左边 ,这确保了当 leftright 相邻时,中点指向 left,从而保证区间能够正确收缩,不会出现 right 原地不动的情况。向下取整 是左端点查找的精髓,也是避免死循环的关键。

二、区间缩进规则

在循环中,根据 arr[mid]arr[mid + 1] 的比较结果,按照以下规则更新指针:

  • arr[mid] < arr[mid + 1] :说明 mid 处于上升段 ,峰值一定在 mid 的右侧。因为 mid 本身不可能是峰值(它小于右边),所以可以安全地将左指针移动到 mid + 1,即舍弃左半区间

  • arr[mid] > arr[mid + 1] :说明 mid 处于下降段 或正好是峰值。此时峰值可能在 mid 左侧,也可能就是 mid 本身。因此将右指针移动到 mid保留 mid 在搜索区间内 ,继续向左寻找。注意这里不能用 right = mid - 1 ,因为 mid 有可能是峰值,必须保留。

三、循环结束后的处理

循环结束时,leftright 相等,此时指向的位置就是第一个满足 arr[i] > arr[i+1] 的位置 ,即峰顶索引。直接返回 left 即可,无需额外判断,因为题目保证数组至少有三个元素且存在峰值。

四、细节注意

  • 循环中比较的是 arr[mid]arr[mid+1],因此需要保证 mid+1 不越界。由于循环条件 left < rightright 初始为 size-1,在循环过程中 mid 最大为 right-1(因为向下取整,当 leftright 相邻时,mid = left,此时 mid+1 = right 合法),所以不会越界。

  • 初始 left = 0right = arr.size() - 1,这覆盖了整个数组。

相关推荐
阿贵---1 小时前
单元测试在C++项目中的实践
开发语言·c++·算法
进击的小头1 小时前
第14篇:MPC控制案例——无人机高度控制
python·算法·无人机
2401_891482172 小时前
C++中的事件驱动编程
开发语言·c++·算法
sw1213892 小时前
C++与Rust交互编程
开发语言·c++·算法
Huyuejia2 小时前
self-attention代码
算法
2401_857918292 小时前
模板编译期机器学习
开发语言·c++·算法
放飞自我的Coder2 小时前
【动态规划解题思路】
算法·动态规划
2403_835568472 小时前
多平台UI框架C++开发
开发语言·c++·算法
yunyun321232 小时前
C++中的适配器模式
开发语言·c++·算法