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

本文介绍了如何利用二分法在C语言中高效地寻找数组中的峰值元素。详细阐述了二分法的思路,通过比较中间元素与相邻元素的关系来逐步缩小搜索范围。文中还给出了具体代码实现,算法的时间复杂度为O(logN),适用于处理大规模数据。
摘要由CSDN通过智能技术生成

C语言好题方法总结。日积月累,慢慢进步!


目录

一、题干

二、题解

※ 二分法寻找峰值

二分法思路

三组图解

代码


一、题干

牛客网链接

NC107 寻找峰值icon-default.png?t=M7J4https://www.nowcoder.com/practice/fcf87540c4f347bcb4cf720b5b350c76

 

 


二、题解

本题也可用暴力破解:遍历数组 [1n-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即可;

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


三组图解

 


 

 代入实例,我们来看:

第一趟比较,确定mid右侧有峰值点,范围左界右移至mid+1

 

第二趟比较,确定mid左边及它本身存在峰值点,right左移至mid

 


 

动态示意图

 


 

代码

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),常数级变量,无额外辅助空间。

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值