1. 问题描述:
峰值元素是指其值大于左右相邻值的元素。给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。你可以假设 nums[-1] = nums[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。
说明:
你的解法应该是 O(logN) 时间复杂度的。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-peak-element
2. 思路分析:
① 分析题目可以知道数组中的元素分布主要存在以下三种情况:a:数组元素都是升序的,b:数组元素都是降序的,c:数组元素中有升序与降序,下面是这三种情况对应的具体图表:
当数组中的元素都是升序的时候那么峰值元素为数组中的最后一个元素,当数组元素为降序的时候那么峰值元素为第一个元素,当数组元素有升有降的时候那么峰值元素在整个数组局部范围中的位置,并且可以发现我们只需要判断nums[i]是否大于nums[i + 1]即可找出数组中的第一个峰值元素,对于a情况在循环中会一直执行到数组元素的末尾,可以发现都满足nums[i] > nums[i + 1],所以我们最终循环执行完之后应该返回数组的最后一个元素的下标即可,对于b情况我们可以发现第一个元素与第二个元素比较之后就满足条件判断所以返回0这个下标,对于c情况:如果有三个相邻的元素a,b,c(b是峰值元素),首先判断a > b是否成立,但是发现不成立,然后会判断下一个元素是否满足b > c,发现满足条件这个时候就返回元素b的下标,而b恰恰是峰值元素所以对于c情况只判断nums[i] > nums[i + 1]也是适用的
② 除了①中的思路之外,力扣的题解中还提供了一种递归二分查找的方法,感觉这种思路很巧妙(但是根据题目要求 O(logN) 时间复杂度所以使用二分查找也不难理解)之前的二分查找数组元素是有序的,而这里因为数组元素是无序的我们则需要根据题目的要求来更新二分查找的范围,我们可以根据中间位置的元素与其左边或者是右边的元素的大小关系来更新下一次二分查找的范围,当判断中间位置的元素与其右边元素的时候,也即nums[mid] < nums[mid + 1],如果满足条件那么更新左范围,下一次二分查找的范围就是[mid + 1, r],因为峰值元素可能是存在右边的,否则nums[mid] > nums[mid + 1],那么峰值元素可能是存在左边的,下一次二分查找的范围为[l, mid],我们可以在更新二分查找范围的时候使用递归的方法进行查找(也可以迭代查找)
③ 对于②中的方法我们可以判断中间位置与左边的元素或者是右边的元素的大小元素,但是这两者的中间值的计算是不一样的,如果判断nums[mid]与nums[mid + 1],那么中间值mid = (l + r) // 2,如果判断nums[mid]与nums[mid - 1]那么中间值mid = (l + r + 1)// 2,其实我们可以举出一个具体的例子会更容易理解,因为在比较nums[mid]与nums[mid - 1]的时候如果mid = (l + r) // 2的话数组会越界,而且有的时候可能会造成死循环,例如这个例子:[1, 2, 1, 3, 5, 6, 4]l = 5,r = 6在循环中范围会一直保持在这里所以导致了死循环,而且可以发现当l, r存在一个为奇数的时候那么加上1除以2之后会移动到下一个位置,,所以当我们判断nums[mid]与nums[mid - 1]的时候计算mid应该使用mid =(l + r + 1)// 2
3. 代码如下:
分析:
from typing import List
class Solution:
def findPeakElement(self, nums: List[int]) -> int:
for i in range(len(nums) - 1):
if nums[i] > nums[i + 1]: return i
return len(nums) - 1
递归二分查找:
class Solution:
def binarySearch(self, l: int, r: int, nums: List[int]):
if l == r: return l
mid = (l + r + 1) // 2
if nums[mid] > nums[mid - 1]:
return self.binarySearch(mid, r, nums)
return self.binarySearch(l, mid - 1, nums)
def findPeakElement(self, nums: List[int]) -> int:
return self.binarySearch(0, len(nums) - 1, nums)
from typing import List
class Solution:
def binarySearch(self, l: int, r: int, nums: List[int]):
if l == r: return l
mid = (l + r) // 2
if nums[mid] < nums[mid + 1]:
return self.binarySearch(mid + 1, r, nums)
return self.binarySearch(l, mid, nums)
def findPeakElement(self, nums: List[int]) -> int:
# 下面是二分递归查找
return self.binarySearch(0, len(nums) - 1, nums)