二分查找(山脉数组的峰顶索引)(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,这覆盖了整个数组。

相关推荐
折哥的程序人生 · 物流技术专研34 分钟前
Java面试85题图解版 · 特别篇:2026后端高频面试题复盘(算法底层逻辑+高并发架构设计全解析,附Java实战代码)
java·网络·数据库·算法·面试
玖玥拾1 小时前
C/C++ 基础笔记(十四)多态与模板编程
c语言·c++·多态·模板
想吃火锅10052 小时前
【leetcode】14.最长公共前缀js
算法·leetcode·职场和发展
Roann_seo%2 小时前
C++文件操作完全指南:从文本读写到二进制文件处理
开发语言·c++
坚果派·白晓明3 小时前
【鸿蒙PC】SDL3 适配:AtomCode + Skills 快速集成 NAPI 测试工具
c++·华为·ai编程·harmonyos·atomcode
云絮.3 小时前
数据库操作
数据库·mysql·算法·oracle
小林ixn3 小时前
LeetCode 206. 反转链表(迭代 + 递归详解)
算法·leetcode·链表
凡人叶枫3 小时前
Effective C++ 条款17:以独立语句将 newed 对象置入智能指针
java·linux·开发语言·c++·算法
凡人叶枫5 小时前
Effective C++ 条款16:成对使用 new 和 delete 时要采取相同形式
开发语言·c++·effective c++
菜鸟‍5 小时前
LeetCode 1 27 和 704 || 两数之和 移除元素 二分查找
算法·leetcode·职场和发展