题目描述
峰值元素是指其值严格大于左右相邻值的元素。
给你一个整数数组 nums
,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞
。
你必须实现时间复杂度为 O(log n)
的算法来解决此问题。
做题情况
- 做出来且思路与标答一致
- 做出来但思路较为复杂 ☑
- 有思路,但时间复杂度较高无法通过
- 没有思路
自己的想法:
二分法自己其实已经想到了,但是如何实践,自己进行的并不好。其实这也不仅仅是二分法写代码自己不是很熟练,而且这种求极值的问题自己套用二分法的时候有点顾此失彼。
标答:
就是标准的二分法。因为是求极值,在一维的情况下,直接进行相邻比较,每次缩减一半的区域就可以了。这和二分法确实不同,二分法有一个介入的数,我们每次都和那个数进行比较。二分法是和下一位进行比较。
现在想想,加一其实是没有什么问题的,因为最极端的情况就是最后以恶搞和倒数第二个,这两个的中间值是前者,还是可以进行加一操作。
下面对二分法进行总结一下吧:
维基百科二分法定义如下
在计算机科学中,二分查找算法也称折半搜索算法,对数搜索算法,是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。
其实并不是找最大值,但是可以用来找局部最优值。
自己对于二分法确实掌握的很不好,无论是应用场景的熟悉度还是代码的具体编写。其实二分法有具体的模板的,自己只需要记住就好了,也不会很难的。
首先对于while()
中的条件,一定是左<右。左和右的起始条件,一定是可以取到的左右值(即0和-1)
循环体中不需要再设置与左右值相关的弹出条件了。
最后返回值是左值。这其实也不绝对,因为最终是两个值相等之后返回的嘛。但是一般来说,因为两个相邻的数相加除以2会回到较小的那个数,所以一般用左值。但这其实相关性也没有很大。
另外是左值和右值向中间变化的时候,只留下我们用的哪一段就可以了,例如:
middle=(begin+end)/2;
if(nums[middle]>nums[middle+1]) end=middle;
else begin=middle+1;
要留下较大的那一部分,对于if
中,是middle
,对于else
中,是middle+1
。
实际代码
class Solution {
public:
int findPeakElement(vector<int>& nums)
{
int n=nums.size();
int begin=0;
int end=n-1;
int middle;
while (begin<end)
{
middle=(begin+end)/2;
if(nums[middle]>nums[middle+1]) end=middle;
else begin=middle+1;
}
return begin;
}
};
总结
一般来说,总结当前使用二分法的情况:
- 对于有序数组
- 对于log(n)或者nlog(n)时间复杂度
- 对于求极值的情况(就是进行所谓的爬坡)
总的来说还是要提高对于二分法的警惕,不然太被动了。