二分查找(山脉数组的峰顶索引)(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。当区间长度为偶数时,中点会偏向左边 ,这确保了当 left 和 right 相邻时,中点指向 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 有可能是峰值,必须保留。

三、循环结束后的处理

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

四、细节注意

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

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

相关推荐
曦樂~7 小时前
Cpp面筋
c语言·c++
道尔柯南7 小时前
C++ string 类详解:从常用接口到深拷贝,彻底掌握字符串处理
android·java·c++
Dream it possible!10 小时前
LeetCode 面试经典 150_多维动态规划_交错字符串(97_C++_中等)
算法·leetcode·面试·动态规划
心抵鹊10 小时前
算法之双指针系列(一)
算法·leetcode
十二10 小时前
【洛谷题解】P1083 [NOIP 2012 提高组] 借教室(二分答案 + 差分)
数据结构·c++·算法·蓝桥杯
小此方11 小时前
[算法实战论:动态规划]回文串问题的dp解法,回文串问题的母题,以及以此为基础的派生题
算法·动态规划
Pniubi12 小时前
力扣560.和为k的子数组
算法·leetcode·职场和发展
D e re k ZH12 小时前
C++ 从零入门(一):基础语法与程序结构
数据结构·c++·算法
Pniubi13 小时前
力扣438.找到字符串中所有字母异位词
算法·leetcode·职场和发展
zjxtxdy14 小时前
C++面向对象程序开发-类与对象
开发语言·c++