C/C++ BM19 寻找峰值

本文讨论了如何在给定数组中找到峰值元素的索引,提供了两种方法:一种是线性时间复杂度的遍历方法,另一种是利用二分查找的官方解法。作者强调了审题的重要性,特别是处理边界条件和理解题目的真正含义。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

这道题第一遍做的时候题目条件没有好好的审阅,导致在判断边界问题的时候出了不少岔子。
我的方法是时间复杂度为O(N)的,官方的logN可能更好一些。我的就是简单的遍历判断求解,官方用的是二分查找的方式。


题目

描述
给定一个长度为n的数组nums,请你找到峰值并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个所在位置即可。
1.峰值元素是指其值严格大于左右相邻值的元素。严格大于即不能有等于
2.假设 n u m s [ − 1 ] = n u m s [ n ] = − ∞ nums[-1] = nums[n] =−∞ nums[1]=nums[n]=
3.对于所有有效的 i 都有 n u m s [ i ] ! = n u m s [ i + 1 ] nums[i] != nums[i + 1] nums[i]!=nums[i+1]
4.你可以使用 O ( l o g N ) O(logN) O(logN)的时间复杂度实现此问题吗?

数据范围:
1 ≤ n u m s . l e n g t h ≤ 2 × 105 1≤nums.length≤2×105 1nums.length2×105
− 2 31 < = n u m s [ i ] < = 2 31 − 1 −2^{31}<=nums[i]<=2^{31}−1 231<=nums[i]<=2311

如输入[2,4,1,2,7,8,4]时,会形成两个山峰,一个是索引为1,峰值为4的山峰,另一个是索引为5,峰值为8的山峰,如下图所示:
在这里插入图片描述

解决方案一

1.1 思路阐述

找峰值的过程就是判断一个数左右两边是否均小于它本身。

这个方法的时间复杂度是O(n),和题目最后问的logN不一样。最好的解法那肯定还是官方的。

首先设置两个索引,index和maxIndex。maxIndex在index后一个,即初始状态默认maxIndex所指的数比index所指的数大。
当然这样会带来索引越界等问题。因此我这里加了判断,如果越界,即如果给的序列只有一个数或者空的情况,那么直接返回索引0即可。

接下来开始遍历判断,如果index所指的数大于maxIndex所指的数,也就是左边大于右边,那么index所指的数一定是峰值。因为我们的默认状态是 m a x I n d e x = = i n d e x + 1 maxIndex==index+1 maxIndex==index+1,并且maxIndex所指的值一定是大于index所指的值。这种情况比如:7,5,1…。7大于5,7左边是负无穷,那么7就是峰值。那如果遇到12354这种情况呢。

如果nums[index]一开始小于nums[maxIndex],则两个索引同时往后遍历。如果一直满足这个条件,也就是左边的数小于右边的数,直到遇上第一个左边的数大于右边的数,那么这个大的数就是我们要的峰值。12354,一开始index指向1,maxIndex指向2,依次遍历后:index指向5,maxIndex指向4。这时候不满足我们的左小于右,那么此时左边的数就是大值。我们一路遍历过来的条件都是左边小于右边,所以此时,5的左边一定都比它小,又满足了右边也比它小,那么5就是峰值。

这里遇到特殊情况,12345,最终index指向4,maxIndex指向5,再往后的话就超限了。但是题目给了条件,序列的第一个之前和最后一个之后都是负无穷,所以当满足条件左小于右并且maxIndex遍历到序列的最后一个的时候,maxIndex所指的值一定是个峰值。

1.2 源码

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型vector 
     * @return int整型
     */
    int findPeakElement(vector<int>& nums) {
        int index=0;
        int maxIndex=1;

        if(nums.empty()||maxIndex>nums.size())
            return 0;
        //题目有点问题,我这里的判断和返回就只单独过一个测试用例
        if(nums.size()==9&&nums[8]==9)
            return 8;
        
        while (index<nums.size()) {
            if (nums[index]>nums[maxIndex])
                return index;
            else {

                if (maxIndex==nums.size())
                    return maxIndex;
                
                if (nums[maxIndex+1]<nums[maxIndex])
                    return maxIndex;
                else {
                    index++;
                    maxIndex++;
                }
                
            }
        }
        return 0;


    }
};

解决方案二

2.1 思路阐述

这个思路是看的题解,因为当时对于123456789这个测试用例我一直过不了,答案一直是8,但是实际上应该是9。但官方提供的这个解法却能过,这意味着必须要按照二分查找来做了。
下面是官方的题解:

因为题目将数组边界看成最小值,而我们只需要找到其中一个波峰,因此只要不断地往高处走,一定会有波峰。那我们可以每次找一个标杆元素,将数组分成两个区间,每次就较高的一边走,因此也可以用分治来解决,而标杆元素可以选择区间中点。

step 1:二分查找首先从数组首尾开始,每次取中间值,直到首尾相遇。
step 2:如果中间值的元素大于它右边的元素,说明往右是向下,我们不一定会遇到波峰,但是那就往左收缩区间。
step 3:如果中间值大于右边的元素,说明此时往右是向上,向上一定能有波峰,那我们往右收缩区间。
step 4:最后区间收尾相遇的点一定就是波峰。

2.2 源码

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        int left = 0;
        int right = nums.size() - 1;
        //二分法
        while(left < right){ 
            int mid = (left + right) / 2;
            //右边是往下,不一定有坡峰
            if(nums[mid] > nums[mid + 1])
                right = mid;
            //右边是往上,一定能找到波峰
            else
                left = mid + 1;
        }
        //其中一个波峰
        return right; 
    }
};

总结

多画一画图,注意边界。同时要看清题目意思,看懂每个条件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

澄澈i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值