寻找峰值

522 阅读1分钟

Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情

一、题目:

162. 寻找峰值

峰值元素是指其值严格大于左右相邻值的元素。

给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。

你可以假设 nums[-1] = nums[n] = -∞ 。

你必须实现时间复杂度为 O(log n) 的算法来解决此问题。

示例 1:

输入:nums = [1,2,3,1]
输出:2
解释:3 是峰值元素,你的函数应该返回其索引 2。

示例 2:

输入:nums = [1,2,1,3,5,6,4]
输出:1 或 5 
解释:你的函数可以返回索引 1,其峰值元素为 2;
     或者返回索引 5, 其峰值元素为 6。

提示:

1 <= nums.length <= 1000
-231 <= nums[i] <= 231 - 1
对于所有有效的 i 都有 nums[i] != nums[i + 1]

二、题解:

1.解读

题目要求在一个nums数组元素中寻找一个峰值元素,峰值元素定义为其值大于左右相邻的两个元素值,并且数组中可能存在多个峰值元素,但是只需要返回其中一个峰值元素的下标即可。对于数组中只有一个元素的、数组元素单调递增或者递减的,可以假设nums[-1] = nums[n] = -∞代表数组两端边界之外为负无穷,然后有1 <= nums.length <= 1000,那么nums数组中必定存在一个峰值元素。题目有nums[i] != nums[i + 1]条件限制,说明任意相邻元素都不相等,所以不用考虑峰值元素的值等于左右相邻的两个元素值的情况。实现算法的时间复杂度限制为O(log n)(眼神暗示二分查找)。

2.思路

二分查找峰值元素下标,所以不要求数组元素有序。二分搜索区间为[left, right],中间元素下标mid,假设mid下标元素可能为峰值元素,那么就判断左右相邻元素与其值的大小。mid + 1不会越界所以首先判断下标mid和下标mid + 1的大小,如果下标mid的元素大于下标mid + 1的元素,根据峰值元素的定义说明下标mid左边元素必定存在峰值元素,而mid右边也只是可能存在峰值元素,所以要往左边移动搜索区间,即将右边界重新定义为下标midmid可能是峰值元素所以不用mid - 1)。反之同理,下标mid的元素小于下标mid + 1的元素那往较大元素的一边移动搜索区间,所以将左边界重新定义为下标mid + 1mid就不可能是峰值元素了)。循环条件为left < right,不管是否经过循环最后left == right,所以返回其中之一即可。

3.实现

首先定义左右边界leftright,初始的分别指向数组两端的元素left = 0right = nums.length - 1,然后循环遍历,定义mid取中间元素下标为mid = (left + right) / 2。判断有nums[mid] > nums[mid + 1],那么应该往[left, mid]区间搜索峰值,同时如果mid == 0或者nums[mid - 1] < nums[mid]说明当前下标mid的元素就是峰值元素可直接返回。否则应该往[mid+1, right]区间搜索峰值。最后循环left < right条件结束返回峰值下标。

三、代码:

class Solution {
    public int findPeakElement(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        while (left < right) {
            int mid = (right + left) / 2;
            if (nums[mid] > nums[mid + 1]) {
                if (mid == 0 || nums[mid - 1] < nums[mid]) {
                    return mid;
                }
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
}

时间复杂度:O(log n) 二分查找的时间复杂度

空间复杂度:O(1) 使用了常数个空间

四、总结

假如没有限制时间复杂度,可以直接单次遍历nums数组,寻找到元素最大值下标或者寻找到一个符合峰值元素条件的下标即可。其实如果没有这个限制,简单理的解题目是在一个数组中查找某个元素,也应该往二分查找方向考虑。

Java中计算mid时为了防止溢出可以写为:mid = left + (right - left) / 2;