一、题目内容
二、题目分析
这道题目其实起码有三种做法,第一种就是最简单的遍历,挨个和左右对比,直到找到峰值元素返回下标即可。但是这种方法运气好的话时间复杂度不过O(1),运气差的话,确是O(N),不过确实是一种容易想到的方法。
public int findPeakElement(int[] nums) {
if(nums.length==1)
return 0;
if(nums[0]>nums[1])
return 0;
if(nums[nums.length-1]>nums[nums.length-2])
return nums.length-1;
for(int i=1;i<nums.length-1;i++)
{
if(nums[i]>nums[i-1]&&nums[i]>nums[i+1])
return i;
}
return -1;
}
讲道理,这么做真的爽,但是确实没有什么意义。
第二种方法,就是寻找数组最大值,因为只要找到最大值,那么最大值肯定是峰值。
但是因为这个数组时无序数组,而且没有规律,所以不能使用时间复杂度为O(logn)的二分,只能老老实实找,时间复杂度还是O(n)。
public int findPeakElement(int[] nums) {
int max=nums[0];
int max_index=0;
int i;
for(i=0;i<nums.length;i++){
if(nums[i]>max){
max=nums[i];
max_index=i;
}
}
return max_index;
}
代码量非常的少,不过也不提倡。
接下来使我们要重点讲的第三种方法,迭代爬坡。
首先,按照二分查找的老套路,我们定义了left=0,right=nums.length-1.然后我们依然取mid等于中间位置,然后不一样的地方来了。我们比较mid左右两边。
nums[mid-1] < nums[mid] > nums[mid+1]时,return mid
nums[mid-1] < nums[mid] < nums[mid+1]时,舍弃左边,left = mid+1
nums[mid-1] > nums[mid] > nums[mid+1]时,舍弃右边,right = mid-1
nums[mid-1] > nums[mid] < nums[mid+1]时,随便舍弃一边即可
上面的原理是什么,首先,如果mid小于右边大于左边,mid右边肯定有符合条件的数!你想,如果从mid开始向右一直递增,那么最后一个数肯定是峰值,如果不是一直递增,而是先递增后递减,那么那个转折的极值点就是峰值!那如果一直递减呢?呵,不可能啊,因为mid+1已经比mid大了,所以这就是原理。
但是上面的做法只是基于本题而言,因为题目说了nums[i]!=nums[i+1]并且当有多个峰值的时候,任意返回一个即可,要是返回下标最小的峰值就没那么简单了。
public int findPeakElement(int[] nums) {
if(nums.length==1)
return 0;
int left = 0, right = nums.length-1;
while(left <= right){
int mid = (left+right)/2;
if(mid==0)
if(mid+1<nums.length&&nums[mid]>nums[mid+1])
return mid;
if(mid==nums.length-1)
if(mid-1>=0&&nums[mid]>nums[mid-1])
return mid;
if(mid-1>=0&&mid+1<nums.length&&nums[mid]>nums[mid-1]&&nums[mid]>nums[mid+1])
return mid;
if(nums[mid]<nums[mid+1]){
left=mid+1;
}
else if(nums[mid]<nums[mid-1])
{
right=mid-1;
}
}
return 0;
}
略微化简后(代码量):
public int findPeakElement(int[] nums) {
int l = 0, r = nums.length - 1;
while (l < r) {
int mid = l + ((r - l) >> 1);
if (nums[mid] < nums[mid+1]) {
l = mid + 1;
} else {
r = mid;
}
}
return l;
}
三、后记
还是决定系统的刷题比较好,今天结束了二分查找部分,明天应该开始双指针。这类题以前也做过不少,大多可以和滑动窗口结合,应该有多解情况。