[LeetCode] Find Peak Element 求数组的局部峰值

 

A peak element is an element that is greater than its neighbors.html

Given an input array nums, where nums[i] ≠ nums[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.post

You may imagine that nums[-1] = nums[n] = -∞.优化

Example 1:url

Input: nums = 
Output: 2
Explanation: 3 is a peak element and your function should return the index number 2.[1,2,3,1]

Example 2:spa

Input: nums = 1,2,1,3,5,6,4]
Output: 1 or 5 
Explanation: Your function can return either index number 1 where the peak element is 2, 
             or index number 5 where the peak element is 6.
[

Note:code

Your solution should be in logarithmic complexity.htm

 

这道题是求数组的一个峰值,若是这里用遍历整个数组找最大值确定会出现Time Limit Exceeded,但题目中说了这个峰值能够是局部的最大值,因此咱们只须要找到第一个局部峰值就能够了。所谓峰值就是比周围两个数字都大的数字,那么只须要跟周围两个数字比较就能够了。既然要跟左右的数字比较,就得考虑越界的问题,题目中给了nums[-1] = nums[n] = -∞,那么咱们其实能够把这两个整型最小值直接加入到数组中,而后从第二个数字遍历到倒数第二个数字,这样就不会存在越界的可能了。因为题目中说了峰值必定存在,那么有一个很重要的corner case咱们要注意,就是当原数组中只有一个数字,且是整型最小值的时候,咱们若是还要首尾垫数字,就会造成一条水平线,从而没有峰值了,因此咱们对于数组中只有一个数字的状况在开头直接判断一下便可,参见代码以下:blog

 

C++ 解法一:ip

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        if (nums.size() == 1) return 0;
        nums.insert(nums.begin(), INT_MIN);
        nums.push_back(INT_MIN);
        for (int i = 1; i < (int)nums.size() - 1; ++i) {
            if (nums[i] > nums[i - 1] && nums[i] > nums[i + 1]) return i - 1;
        }
        return -1;
    }
};

 

Java 解法一:

class Solution {
    public int findPeakElement(int[] nums) {
        if (nums.length == 1) return 0;
        int[] newNums = new int[nums.length + 2];
        System.arraycopy(nums, 0, newNums, 1, nums.length);
        newNums[0] = Integer.MIN_VALUE;
        newNums[newNums.length - 1] = Integer.MIN_VALUE;
        for (int i = 1; i < newNums.length - 1; ++i) {
            if (newNums[i] > newNums[i - 1] && newNums[i] > newNums[i + 1]) return i - 1;
        }
        return -1;
    }
}

 

咱们能够对上面的线性扫描的方法进行一些优化,能够省去首尾垫值的步骤。因为题目中说明了局部峰值必定存在,那么实际上能够从第二个数字开始日后遍历,若是第二个数字比第一个数字小,说明此时第一个数字就是一个局部峰值;不然就日后继续遍历,如今是个递增趋势,若是此时某个数字小于前面那个数字,说明前面数字就是一个局部峰值,返回位置便可。若是循环结束了,说明原数组是个递增数组,返回最后一个位置便可,参见代码以下:

 

C++ 解法二:

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        for (int i = 1; i < nums.size(); ++i) {
            if (nums[i] < nums[i - 1]) return i - 1;
        }
        return nums.size() - 1;
    }
};

   

Java 解法二:

public class Solution {
    public int findPeakElement(int[] nums) {
        for (int i = 1; i < nums.length; ++i) {
            if (nums[i] < nums[i - 1]) return i - 1;
        }
        return nums.length - 1;
    }
}

 

因为题目中提示了要用对数级的时间复杂度,那么咱们就要考虑使用相似于二分查找法来缩短期,因为只是须要找到任意一个峰值,那么咱们在肯定二分查找折半后中间那个元素后,和紧跟的那个元素比较下大小,若是大于,则说明峰值在前面,若是小于则在后面。这样就能够找到一个峰值了,代码以下:

 

C++ 解法三:

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

 

Java 解法三:

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

 

相似题目:

Peak Index in a Mountain Array

 

参考资料:

https://leetcode.com/problems/find-peak-element

https://leetcode.com/problems/find-peak-element/discuss/50232/find-the-maximum-by-binary-search-recursion-and-iteration

 

LeetCode All in One 题目讲解汇总(持续更新中...)

相关文章
相关标签/搜索