DAY-6 | 牛客-NC107 寻找峰值:二分法巧得峰值

简介: 这是一个关于寻找数组峰值的编程问题,题目来源于牛客网的 NC107 题目。文章介绍了两种方法,其中重点讲解了使用二分法寻找峰值的思路:通过比较数组中点与相邻元素的大小,不断缩小搜索范围,直到找到峰值或范围只剩一个元素。二分法的时间复杂度为 O(logN),空间复杂度为 O(1)。文章还配有多张图解和代码示例来辅助理解。

一、题干


牛客网链接


NC107 寻找峰值

https://www.nowcoder.com/practice/fcf87540c4f347bcb4cf720b5b350c76






二、题解


本题也可用暴力破解:遍历数组 [1,n-1] 号位置,哪个位置的数据同时大于上一个和下一个,该数即为峰值。暴力破解法在此不多讨论,本文主要介绍一种更优的算法:二分法。


二分法寻找峰值


二分法思路


通过比较左右位置与中间值的大小关系来限定寻找范围,从而确定峰值可能出现的位置。具体思路如下,注意,由于只需要找出全数组的一个峰值,因此不管是从右往左看还是从左往右看,其本质是一样的。以下我们以从右往左看的角度来分析。


首先考虑一般情况(即数组元素个数大于1,且两边边界都是非峰值点的情况):


1. 若数组中间位置(mid)的值比它右边位置(mid + 1)的大,则认为从它最右边(right)到中间(mid)的这右半部分的数值从右向左递增的。因而,左半部分一定有一个峰值我们要把寻找范围向左缩小,即把 right 不断向左靠拢: right = mid。


注意:不能是 right = mid-1 ,因为我们认为数组从 right 向 mid 递增,而 mid 这个位置的值也有可能就是峰值。若直接 right = mid - 1,有可能把峰值错过。

2. 当遇到 mid 位置的值比它右边(mid + 1)的小了,则可以当作数值从右向左开始递减,说明此时峰值一定在右半部分。这时将 left 向右移:left = mid + 1(将 left 移到 mid 的右边一个元素)。


注意:这里 left 不断右移的目的是确保范围右半部分数均为自右向左递增,right 不断左移的目的是确保范围左半部分数均自右向左递减。遍历到最后,范围的左界 left 不再小于右界 right 时,说明查找结束,最后留下的就是峰值。


3. 由此不断判断--移动 right 或 left 缩减范围。当范围缩至 mid+1 的位置大于等于 right 位置时,意味着这个 mid+1 位置是一个分界点:


在该位置的左半边,数值均自右往左递减;


在该位置的右半边,数值均自右往左递增。


4. 从而,此时的 mid + 1 位置,就是我们要找的峰值点。由于 left = mid + 1,因此最后返回的left值,就是峰值点所在位置。


以上为一般情况。但注意,当数组元素个数只有1个,或者首尾元素出现峰值时,这个方法就不适用了,需要特殊考虑。由于情况较少,所以并不难处理。


若数组元素个数只有一个,只需返回下标0即可;


若判断出首尾元素有峰值,则直接返回首尾元素下标即可。







代入实例,我们来看:








动态示意图


代码


int findPeakElement(int* nums, int numsLen ) { 
    //边界情况处理,1个元素前后都是负无穷 以及 0号位置大于1号位置,-1位置负无穷的情况 
    if (numsLen == 1 || nums[0] > nums[1]) 
        return 0; 
 
    //末尾位置数据大于上一个位置数据,而nums[numsLen]负无穷的情况 
    if (nums[numsLen-1] > nums[numsLen-2]) 
        return numsLen-1; 
 
    int left = 0, right = numsLen - 1, mid;
    while(left < right) { 
        mid = left + (right - left) / 2; 
        if (nums[mid] < nums[mid + 1])//中间比右边小,意味着右边肯定有个峰值
            left = mid + 1; 
        else //否则在左边包括当前位置肯定有个峰值 
            right = mid; 
    }
    return left; 
}


算法时间复杂度为O(logN),二分法最坏情况对整个数组连续二分,最多能分logN次。空间复杂度为O(1),常数级变量,无额外辅助空间。



相关文章
【面试必刷TOP101】寻找峰值 & 数组中的逆序对
【面试必刷TOP101】寻找峰值 & 数组中的逆序对
59 0
|
3月前
|
算法
【算法】二分算法——寻找峰值
【算法】二分算法——寻找峰值
|
5月前
|
算法 数据可视化 数据挖掘
深入解析力扣162题:寻找峰值(线性扫描与二分查找详解)
深入解析力扣162题:寻找峰值(线性扫描与二分查找详解)
|
6月前
每日一题(寻找奇数,寻找峰值)
每日一题(寻找奇数,寻找峰值)
27 0
|
6月前
【每日一题Day167】LC1000合并石头的最低成本 | 区间dp
【每日一题Day167】LC1000合并石头的最低成本 | 区间dp
55 1
【每日一题Day167】LC1000合并石头的最低成本 | 区间dp
|
6月前
leetcode-871:最低加油次数
leetcode-871:最低加油次数
31 0
|
算法 容器
华为机试HJ99:自守数(附带提速方案)
华为机试HJ99:自守数(附带提速方案)
|
人工智能
蓝桥 k倍区间 (前缀和)
蓝桥 k倍区间 (前缀和)
|
人工智能
蓝桥杯:k倍区间
蓝桥杯:k倍区间
60 0
|
算法 C++ Python
【每日算法Day 91】求解数组中出现次数超过1/3的那个数
【每日算法Day 91】求解数组中出现次数超过1/3的那个数