LeetCode - 162.寻找峰值

151 阅读2分钟

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. nums[i1]<nums[i]>nums[i+1]nums[i-1] \lt nums[i] \gt nums[i+1],此时说明处于山峰,即 ii 是峰值元素索引
  2. nums[i1]<nums[i]<nums[i+1]nums[i-1] \lt nums[i] \lt nums[i+1],此时说明处于上坡阶段ii 值应往右走,即 ii+1i \rightarrow i+1
  3. nums[i1]>nums[i]>nums[i+1]nums[i-1] \gt nums[i] \gt nums[i+1],此时说明处于下坡阶段ii 值应往左走,即 ii1i \rightarrow i-1
  4. nums[i1]>nums[i]<nums[i+1]nums[i-1] \gt nums[i] \lt nums[i+1],此时说明处于山谷,左右两边都可,此处假设往右走

排除情况 1 后,可得到以下的结论:

  • nums[i]<nums[i+1]nums[i] \lt nums[i+1],则往右走,即 ii+1i \rightarrow i+1
  • nums[i]>nums[i+1]nums[i] \gt nums[i+1],则往左走,即 ii1i \rightarrow i-1

为了完成以上任务,需要定义以下两个函数:

  • int[] get(int[] nums,int idx):该方法是一个辅助函数,用以识别 idx 越界的情况;当 idx=-1 or idx=n-1 时,返回 [0,0][0,0];否则,返回 [1,nums[idx]][1,nums[idx]]
  • int compare(int[] nums,int idx1,int idx2) :比较函数,返回 -1 表示小于,返回 0 表示等于,返回 1 表示大于
    • 首先获取通过 getget 方法获取 idx1idx2 的函数值,分别为 nums1nums2
    • 判断 nums1[0] != nums2[0];只有在 idx 分别为 [1,0][-1,0][n1,n][n-1,n] 时出现,即移动到边界的情况
      • nums1[0]<nums2[0]nums1[0]<nums2[0] 时,该情况出现在 [1,0][-1,0],此时应当往右走,所以返回 -1
      • nums1[0]>nums2[0]nums1[0]>nums2[0] 时,该情况出现在 [n1,n][n-1,n],此时应当往左走,所以返回 1
    • nums1[1]=nums2[1]nums1[1]=nums2[1] 时,返回 0
    • nums1[1]<nums2[1]nums1[1] \lt nums2[1] 时,则往右走,返回 -1
    • nums1[1]>nums2[1]nums1[1] \gt nums2[1] 时,则往左走,返回 1

例子:

数组 nums=[1,2,1,3,5,6,4]nums = [1,2,1,3,5,6,4] 趋势图如下所示。明显山峰位置的索引在 1155

趋势图.png

162寻找峰值.png

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;
}