1.题目简述
给定一个整数数组数组 nums[a1,a2,..,an],找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
峰值元素是指其值严格大于左右相邻值的元素。例如 a1<a2 && a2>a3,则 a2 是峰值。
例子:
输入:nums = [1,2,1,3,5,6,4]
输出:1 或 5
解释:你的函数可以返回索引 1,其峰值元素为 2; 或者返回索引 5, 其峰值元素为 6。
详情请查看 LeetCode 官网:162. 寻找峰值 - 力扣(LeetCode)
2.方法
根据题目题意,可总结以下几点:
- 当 ,此时说明处于山峰,即 是峰值元素索引
- 当 ,此时说明处于上坡阶段, 值应往右走,即
- 当 ,此时说明处于下坡阶段, 值应往左走,即
- 当 ,此时说明处于山谷,左右两边都可,此处假设往右走
排除情况 1 后,可得到以下的结论:
- 当 ,则往右走,即
- 当 ,则往左走,即
为了完成以上任务,需要定义以下两个函数:
int[] get(int[] nums,int idx):该方法是一个辅助函数,用以识别 idx 越界的情况;当 idx=-1 or idx=n-1 时,返回 ;否则,返回int compare(int[] nums,int idx1,int idx2):比较函数,返回 -1 表示小于,返回 0 表示等于,返回 1 表示大于- 首先获取通过 方法获取 idx1 和 idx2 的函数值,分别为 nums1 和 nums2
- 判断 nums1[0] != nums2[0];只有在 idx 分别为 或 时出现,即移动到边界的情况
- 当 时,该情况出现在 ,此时应当往右走,所以返回 -1
- 当 时,该情况出现在 ,此时应当往左走,所以返回 1
- 当 时,返回 0
- 当 时,则往右走,返回 -1
- 当 时,则往左走,返回 1
例子:
数组 趋势图如下所示。明显山峰位置的索引在 和 。
3.代码实现
public int findPeakElement(int[] nums) {
int n = nums.length;
int ans = -1;
int left = 0,right = n-1;
while(left <= right){
int mid = left + (right-left)/2;
// 已找到山峰
if(compare(nums,mid-1,mid)<0 && compare(nums,mid,mid+1)>0){
ans = mid;
break;
}
if(compare(nums,mid,mid+1) < 0){
// 情况 2 或 4,往右走
left = mid+1;
}else{
// 情况 3,往左走
right = mid-1;
}
}
return ans;
}
// 处理索引越界的情况,方便比较函数识别这种情况
int[] get(int[] nums,int idx){
if(idx==-1 || idx==nums.length){
return new int[]{0,0};
}
return new int[]{1,nums[idx]};
}
int compare(int[] nums,int idx1,int idx2){
int[] nums1 = get(nums,idx1);
int[] nums2 = get(nums,idx2);
// 识别 [-1,0] 和 [n-1,n] 的情况
if(nums1[0] != nums2[0]){
// nums1[0]<nums2[0],是 [-1,0] 的情况,返回 -1,使 i->i+1
// nums1[0]>nums2[0],是 [n-1,n] 的情况,返回 1,使 i->i-1
return nums1[0] > nums2[0] ? 1:-1;
}
if(nums1[1] == nums2[1]){
return 0;
}
return nums1[1] < nums2[1] ? -1:1;
}