题号:162 题目
A peak element is an element that is greater than its neighbors.
Given an input array where num[i] ≠ num[i+1]
, find a peak element and return its index.
The array may contain multiple peaks, in that case return the index to any one of the peaks is fine.
You may imagine that num[-1] = num[n] = -∞
.
For example, in array [1, 2, 3, 1]
, 3 is a peak element and your function should return the index number 2.
Note:
Your solution should be in logarithmic complexity.
题目分析:从时间复杂度上要求是logN,这样首先想到的就是用二分查找。开始查中间的数,看是否是peak点,如果不是,那怎么办呢?往前找还是往后找?因为峰值可能在前面也可能在后面,我开始的想法是先往前找,接着再往后找,于是写了下面的程序:
public int findPeakElement(int[] num){
return findPeakElement1(num,0,num.length-1);
}
public static int findPeakElement1(int[] num,int start,int end) {
if (start <=end) {
int middle=(start+end)/2;
if(isPeak(num, middle)) return middle;
else {
int post = findPeakElement1(num, start, middle - 1);
int after = findPeakElement1(num, middle + 1, end);
return post==-1?after:post;
}
}else {
return -1;
}
}
public static boolean isPeak(int[] num,int peak){
int post = (peak == 0) ? Integer.MIN_VALUE : num[peak - 1];
int after = (peak == num.length - 1) ? Integer.MIN_VALUE
: num[peak + 1];
if (num[peak] >=post && num[peak] >=after) {
return true;
}
return false;
}
该程序的运行时间是222ms。这里虽然用了二分查找,但是如果运气不好,在数组的前面找不到峰值,就得去后面找,这样几乎是遍历了整个数组,时间还是很长。那我们有没有什么办法判断一下,只对数组的前半部分或者后半部分进行查找呢?答案是可以的,我们再看题目的:题目中说如果有多个峰值,返回其中的一个峰值即可。我们考虑中间元素与左右元素的关系,如果右边的元素比中间元素大,那么我们一定能够在右边找到一个峰值,因为num[n]是负无穷,那我们可以完全不用理会左边的元素是怎样的,只遍历右边即可。同样的情况也适用于左边。这样我们每次只用对一半的数组进行遍历即可,查找次数变少了。
代码如下:
/**
* @Date:2013-3-13
* @author:renxiao
* */
public class Solution {
public int findPeakElement(int[] num){
int start=0,end=num.length-1;
while(start<end){
int middle=(start+end)/2;
int post = (middle == 0) ? Integer.MIN_VALUE : num[middle - 1];
int after = (middle == num.length - 1) ? Integer.MIN_VALUE
: num[middle + 1];
if (num[middle] >post && num[middle] >after) {
return middle;
}else if(num[middle]<after){
start=middle+1;
}else{
end=middle-1;
}
}
return start;
}
}
代码整洁很多了。